/*******************************************************************************
 * Copyright (c) 2025-07-25 @author <a href="mailto:iffiff1@gmail.com">Tyler Chen</a>.
 * All rights reserved.
 *
 * Contributors:
 *     <a href="mailto:iffiff1@gmail.com">Tyler Chen</a> - initial API and implementation.
 ******************************************************************************/
package org.iff.util;

import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.*;
import java.util.Calendar;
import java.util.Date;
import java.util.Map;
import java.util.Objects;
import java.util.function.Consumer;

/**
 * DateUtils
 *
 * @author <a href="mailto:iffiff1@gmail.com">Tyler Chen</a>
 * @since 2025-07-25
 */
public class DateUtils {
    static final Map<Integer/*len-T-splash-dash-dot-colon*/, Formatter> formatters = (Map<Integer, Formatter>) Utils.toMap(
            key("yyyy/MM/dd'T'HH:mm:ss.SSS"), Formatter.ofPattern("yyyy/MM/dd'T'HH:mm:ss.SSS"),
            key("yyyy/M/d'T'H:mm:ss.SSS"), Formatter.ofPattern("yyyy/M/d'T'H:mm:ss.SSS"),
            key("yyyy/M/d'T'H:mm:ss.SSS", 1), Formatter.ofPattern("yyyy/M/d'T'H:mm:ss.SSS"),
            key("yyyy/M/d'T'H:mm:ss.SSS", 2), Formatter.ofPattern("yyyy/M/d'T'H:mm:ss.SSS"),
            key("yyyy/MM/dd'T'HH:mm:ss"), Formatter.ofPattern("yyyy/MM/dd'T'HH:mm:ss"),
            key("yyyy/MM/dd'T'HH:mm"), Formatter.ofPattern("yyyy/MM/dd'T'HH:mm"),
            key("yyyy/M/d'T'H:mm:ss"), Formatter.ofPattern("yyyy/M/d'T'H:mm:ss"),
            key("yyyy/M/d'T'H:mm:ss", 1), Formatter.ofPattern("yyyy/M/d'T'H:mm:ss"),
            key("yyyy/M/d'T'H:mm:ss", 2), Formatter.ofPattern("yyyy/M/d'T'H:mm:ss"),
            key("yyyy/M/d'T'H:mm"), Formatter.ofPattern("yyyy/M/d'T'H:mm"),
            key("yyyy/M/d'T'H:mm", 1), Formatter.ofPattern("yyyy/M/d'T'H:mm"),
            key("yyyy/M/d'T'H:mm", 2), Formatter.ofPattern("yyyy/M/d'T'H:mm"),
            key("yyyy-MM-dd'T'HH:mm:ss.SSS"), Formatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS"),
            key("yyyy-M-d'T'H:mm:ss.SSS"), Formatter.ofPattern("yyyy-M-d'T'H:mm:ss.SSS"),
            key("yyyy-MM-dd'T'HH:mm:ss"), Formatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"),
            key("yyyy-MM-dd'T'HH:mm"), Formatter.ofPattern("yyyy-MM-dd'T'HH:mm"),
            key("yyyy-M-d'T'H:mm:ss"), Formatter.ofPattern("yyyy-M-d'T'H:mm:ss"),
            key("yyyy-M-d'T'H:mm"), Formatter.ofPattern("yyyy-M-d'T'H:mm"),
            key("yyyy/MM/dd HH:mm:ss.SSS"), Formatter.ofPattern("yyyy/MM/dd HH:mm:ss.SSS"),
            key("yyyy/M/d H:mm:ss.SSS"), Formatter.ofPattern("yyyy/M/d H:mm:ss.SSS"),
            key("yyyy/M/d H:mm:ss.SSS", 1), Formatter.ofPattern("yyyy/M/d H:mm:ss.SSS"),
            key("yyyy/M/d H:mm:ss.SSS", 2), Formatter.ofPattern("yyyy/M/d H:mm:ss.SSS"),
            key("yyyy/MM/dd HH:mm:ss"), Formatter.ofPattern("yyyy/MM/dd HH:mm:ss"),
            key("yyyy/MM/dd HH:mm"), Formatter.ofPattern("yyyy/MM/dd HH:mm"),
            key("yyyy/M/d H:mm:ss"), Formatter.ofPattern("yyyy/M/d H:mm:ss"),
            key("yyyy/M/d H:mm:ss", 1), Formatter.ofPattern("yyyy/M/d H:mm:ss"),
            key("yyyy/M/d H:mm:ss", 2), Formatter.ofPattern("yyyy/M/d H:mm:ss"),
            key("yyyy/M/d H:mm"), Formatter.ofPattern("yyyy/M/d H:mm"),
            key("yyyy/M/d H:mm", 1), Formatter.ofPattern("yyyy/M/d H:mm"),
            key("yyyy/M/d H:mm", 2), Formatter.ofPattern("yyyy/M/d H:mm"),
            key("yyyy/MM/dd"), Formatter.ofPattern("yyyy/MM/dd"),
            key("yyyy/M/d"), Formatter.ofPattern("yyyy/M/d"),
            key("yyyy/M/d", 1), Formatter.ofPattern("yyyy/M/d"),
            key("yyyy-MM-dd HH:mm:ss.SSS"), Formatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"),
            key("yyyy-M-d H:mm:ss.SSS"), Formatter.ofPattern("yyyy-M-d H:mm:ss.SSS"),
            key("yyyy-MM-dd HH:mm:ss"), Formatter.ofPattern("yyyy-MM-dd HH:mm:ss"),
            key("yyyy-MM-dd HH:mm"), Formatter.ofPattern("yyyy-MM-dd HH:mm"),
            key("yyyy-M-d H:mm:ss"), Formatter.ofPattern("yyyy-M-d H:mm:ss"),
            key("yyyy-M-d H:mm"), Formatter.ofPattern("yyyy-M-d H:mm"),
            key("yyyy-MM-dd"), Formatter.ofPattern("yyyy-MM-dd"),
            key("yyyy-M-d"), Formatter.ofPattern("yyyy-M-d"),
            key("yyyyMMdd HH:mm"), Formatter.ofPattern("yyyyMMdd HH:mm"),
            key("yyyyMMdd H:mm"), Formatter.ofPattern("yyyyMMdd H:mm"),
            key("yyyyMMddHHmmss"), Formatter.ofPattern("yyyyMMddHHmmss"),
            key("yyyyMMddHHmm"), Formatter.ofPattern("yyyyMMddHHmm"),
            key("yyyyMMddHH"), Formatter.ofPattern("yyyyMMddHH"),
            key("yyyyMMdd"), Formatter.ofPattern("yyyyMMdd")
    );

    static final ZoneId[] ZONE_ID = new ZoneId[]{ZoneId.systemDefault()};

    static class Formatter {
        private DateTimeFormatter formatter;
        private String pattern;

        public static Formatter ofPattern(String pattern) {
            return new Formatter(DateTimeFormatter.ofPattern(pattern), pattern);
        }

        public Formatter(DateTimeFormatter formatter, String pattern) {
            this.formatter = Objects.requireNonNull(formatter);
            this.pattern = Objects.requireNonNull(pattern);
        }

        public DateTimeFormatter getFormatter() {
            return formatter;
        }

        public String getPattern() {
            return pattern;
        }
    }

    /**
     * 计算日期的组成形式：长度-T-斜杠-横杠-点-冒号
     * len-T-splash-dash-dot-colon
     */
    static Integer key(String content) {
        return key(content, 0);
    }

    /**
     * 计算日期的组成形式：长度-T-斜杠-横杠-点-冒号
     * len-T-splash-dash-dot-colon
     */
    static Integer key(String content, int offset) {
        int len = content.length();
        int t = 0, splash = 0, dash = 0, colon = 0, dot = 0, sq = 0;
        for (int i = 0; i < len; i++) {
            char c = content.charAt(i);
            switch (c) {
                case 'T':
                    t++;
                    break;
                case '/':
                    splash++;
                    break;
                case '-':
                    dash++;
                    break;
                case ':':
                    colon++;
                    break;
                case '.':
                    dot++;
                    break;
                case '\'':
                    sq++;
                    break;
                default:
                    break;
            }
        }
        return (len + offset - sq) * 100000 + t * 10000 + splash * 1000 + dash * 100 + dot * 10 + colon;
    }

    public static void setDefaultZoneId(ZoneId zoneId) {
        ZONE_ID[0] = Objects.requireNonNull(zoneId);
    }

    public static String format(LocalDateTime date, String pattern) {
        if (date == null) {
            return null;
        }
        return DateTimeFormatter.ofPattern(pattern).withZone(ZONE_ID[0]).format(date);
    }

    public static String format(LocalDate date, String pattern) {
        if (date == null) {
            return null;
        }
        return DateTimeFormatter.ofPattern(pattern).withZone(ZONE_ID[0]).format(date.atStartOfDay());
    }

    public static String format(ZonedDateTime date, String pattern) {
        if (date == null) {
            return null;
        }
        return DateTimeFormatter.ofPattern(pattern).withZone(ZONE_ID[0]).format(date);
    }

    public static String format(Instant date, String pattern) {
        if (date == null) {
            return null;
        }
        return DateTimeFormatter.ofPattern(pattern).withZone(ZONE_ID[0]).format(date);
    }

    public static String format(Date date, String pattern) {
        return date == null ? null : new SimpleDateFormat(pattern).format(date);
    }

    public static String format(Calendar date, String pattern) {
        return date == null ? null : new SimpleDateFormat(pattern).format(date.getTime());
    }

    public static String format(LocalDateTime date) {
        return format(date, "yyyy-MM-dd HH:mm:ss");
    }

    public static String format(LocalDate date) {
        return format(toLocalDateTime(date), "yyyy-MM-dd HH:mm:ss");
    }

    public static String format(ZonedDateTime date) {
        return format(date, "yyyy-MM-dd HH:mm:ss");
    }

    public static String format(Instant date) {
        return format(date, "yyyy-MM-dd HH:mm:ss");
    }

    public static String format(Date date) {
        return format(date, "yyyy-MM-dd HH:mm:ss");
    }

    public static String format(Calendar date) {
        return format(date, "yyyy-MM-dd HH:mm:ss");
    }

    public static String formatWithoutTime(LocalDateTime date) {
        return format(date, "yyyy-MM-dd");
    }

    public static String formatWithoutTime(LocalDate date) {
        return format(date, "yyyy-MM-dd");
    }

    public static String formatWithoutTime(ZonedDateTime date) {
        return format(date, "yyyy-MM-dd");
    }

    public static String formatWithoutTime(Instant date) {
        return format(date, "yyyy-MM-dd");
    }

    public static String formatWithoutTime(Date date) {
        return format(date, "yyyy-MM-dd");
    }

    public static LocalDateTime parse(String dateStr) {
        int key = key(dateStr);
        Formatter formatter = formatters.get(key);
        if (formatter != null) {
            if (key % 10 == 0) {//is date without time
                return parseNoTime(dateStr, formatter.getFormatter());
            } else {
                return parse(dateStr, formatter.getFormatter());
            }
        }
        throw new RuntimeException("Could not parse date [" + dateStr + "]");
    }

    public static LocalDate parseLocalDate(String dateStr) {
        LocalDateTime parse = parse(dateStr);
        return parse.toLocalDate();
    }

    public static Date parseDate(String dateStr) {
        return toDate(parse(dateStr));
    }

    public static Calendar parseCalendar(String dateStr) {
        return toCalendar(parse(dateStr));
    }

    public static Instant parseInstant(String dateStr) {
        return toInstant(parse(dateStr));
    }

    static LocalDateTime parse(String dateStr, DateTimeFormatter formatter) {
        try {
            return LocalDateTime.parse(dateStr, formatter.withZone(ZONE_ID[0]));
        } catch (Exception e) {
            throw new RuntimeException("Could not parse date [" + dateStr + "], pattern[" + formatter + "]", e);
        }
    }

    static LocalDateTime parseNoTime(String dateStr, DateTimeFormatter formatter) {
        try {
            LocalDateTime parse = LocalDate.parse(dateStr, formatter.withZone(ZONE_ID[0])).atStartOfDay();
            return parse;
        } catch (Exception e) {
            throw new RuntimeException("Could not parse date [" + dateStr + "], pattern[" + formatter + "]", e);
        }
    }

    /**
     * @return time in millisecond
     */
    public static Long getTime(Temporal date) {
        if (date == null) {
            return null;
        }
        if (date instanceof Instant) {
            Instant ins = (Instant) date;
            return ins.getEpochSecond() * 1000 + ins.getNano() / 1_000_000;
        } else if (date instanceof LocalDate) {
            return date.getLong(ChronoField.EPOCH_DAY) * 24 * 60 * 60 * 1000;
        } else if (date instanceof LocalDateTime) {
            return date.getLong(ChronoField.EPOCH_DAY) * 24 * 60 * 60 * 1000 + date.getLong(ChronoField.MILLI_OF_DAY);
        } else if (date instanceof LocalTime) {
            return ((LocalTime) date).toNanoOfDay() / 1_000_000;
        } else if (date instanceof ZonedDateTime) {
            return date.getLong(ChronoField.EPOCH_DAY) * 24 * 60 * 60 * 1000 + date.getLong(ChronoField.MILLI_OF_DAY);
        } else if (date.isSupported(ChronoField.EPOCH_DAY) && date.isSupported(ChronoField.MILLI_OF_DAY)) {
            return date.getLong(ChronoField.EPOCH_DAY) * 24 * 60 * 60 * 1000 + date.getLong(ChronoField.MILLI_OF_DAY);
        } else if (date.isSupported(ChronoField.INSTANT_SECONDS) && date.isSupported(ChronoField.MILLI_OF_SECOND)) {
            return date.getLong(ChronoField.INSTANT_SECONDS) * 1000 + date.getLong(ChronoField.MILLI_OF_SECOND);
        } else if (date.isSupported(ChronoField.EPOCH_DAY)) {
            return date.getLong(ChronoField.EPOCH_DAY) * 24 * 60 * 60 * 1000;
        }
        return null;
    }

    /**
     * @return time in millisecond
     */
    public static Long getTime(Calendar date) {
        return date == null ? null : date.getTimeInMillis();
    }

    /**
     * @return time in millisecond
     */
    public static Long getTime(Date date) {
        return date == null ? null : date.getTime();
    }


    public static LocalDateTime toLocalDateTime(LocalDateTime date) {
        return date;
    }

    public static LocalDateTime toLocalDateTime(LocalDate date) {
        return date == null ? null : date.atStartOfDay();
    }

    public static LocalDateTime toLocalDateTime(Instant date) {
        return date == null ? null : date.atZone(ZONE_ID[0]).toLocalDateTime();
    }

    public static LocalDateTime toLocalDateTime(ZonedDateTime date) {
        return date == null ? null : date.toLocalDateTime();
    }

    public static LocalDateTime toLocalDateTime(Date date) {
        return date == null ? null : date.toInstant().atZone(ZONE_ID[0]).toLocalDateTime();
    }

    public static LocalDateTime toLocalDateTime(Calendar date) {
        return date == null ? null : date.getTime().toInstant().atZone(ZONE_ID[0]).toLocalDateTime();
    }

    public static LocalDateTime toLocalDateTime(Long millisecond) {
        return millisecond == null ? null : toLocalDateTime(new Date(millisecond));
    }

    public static LocalDate toLocalDate(LocalDateTime date) {
        return date == null ? null : date.toLocalDate();
    }

    public static LocalDate toLocalDate(LocalDate date) {
        return date;
    }

    public static LocalDate toLocalDate(Instant date) {
        return date == null ? null : date.atZone(ZONE_ID[0]).toLocalDate();
    }

    public static LocalDate toLocalDate(ZonedDateTime date) {
        return date == null ? null : date.toLocalDate();
    }

    public static LocalDate toLocalDate(Date date) {
        return date == null ? null : toLocalDateTime(date).toLocalDate();
    }

    public static LocalDate toLocalDate(Calendar date) {
        return date == null ? null : toLocalDateTime(date).toLocalDate();
    }

    public static LocalDate toLocalDate(Long millisecond) {
        return millisecond == null ? null : toLocalDateTime(millisecond).toLocalDate();
    }

    public static Instant toInstant(LocalDateTime date) {
        return date == null ? null : date.atZone(ZONE_ID[0]).toInstant();
    }

    public static Instant toInstant(LocalDate date) {
        return date == null ? null : date.atStartOfDay().atZone(ZONE_ID[0]).toInstant();
    }

    public static Instant toInstant(ZonedDateTime date) {
        return date == null ? null : date.toInstant();
    }

    public static Instant toInstant(Instant date) {
        return date;
    }

    public static Instant toInstant(Date date) {
        return toInstant(toLocalDateTime(date));
    }

    public static Instant toInstant(Calendar date) {
        return toInstant(toLocalDateTime(date));
    }

    public static Instant toInstant(Long millisecond) {
        return toInstant(toLocalDateTime(millisecond));
    }

    public static Date toDate(LocalDateTime date) {
        return date == null ? null : Date.from(date.atZone(ZONE_ID[0]).toInstant());
    }

    public static Date toDate(LocalDate date) {
        return date == null ? null : Date.from(date.atStartOfDay().atZone(ZONE_ID[0]).toInstant());
    }

    public static Date toDate(ZonedDateTime date) {
        return toDate(date.toLocalDateTime());
    }

    public static Date toDate(Instant date) {
        return date == null ? null : Date.from(date);
    }

    public static Date toDate(Date date) {
        return date;
    }

    public static Date toDate(Calendar date) {
        return date == null ? null : date.getTime();
    }

    public static Date toDate(Long millisecond) {
        return millisecond == null ? null : new Date(millisecond);
    }

    public static Calendar toCalendar(LocalDateTime date) {
        return date == null ? null : toCalendar(toDate(date));
    }

    public static Calendar toCalendar(LocalDate date) {
        return date == null ? null : toCalendar(toDate(date));
    }

    public static Calendar toCalendar(ZonedDateTime date) {
        return date == null ? null : toCalendar(toDate(date));
    }

    public static Calendar toCalendar(Instant date) {
        return date == null ? null : toCalendar(toDate(date));
    }

    public static Calendar toCalendar(Date date) {
        if (date == null) {
            return null;
        }
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        return cal;
    }

    public static Calendar toCalendar(Calendar date) {
        return date;
    }

    public static Calendar toCalendar(Long millisecond) {
        if (millisecond == null) {
            return null;
        }
        Calendar cal = Calendar.getInstance();
        cal.setTimeInMillis(millisecond);
        return cal;
    }
    //

    public static LocalDateTime fromIntLocalDateTime(Integer yyyyMMdd) {
        if (yyyyMMdd == null) {
            return null;
        }
        int dd = yyyyMMdd % 100;
        int MM = yyyyMMdd / 100 % 100;
        int yy = yyyyMMdd / 100_00;
        return LocalDateTime.of(yy, MM, dd, 0, 0, 0, 0);
    }

    public static LocalDate fromIntLocalDate(Integer yyyyMMdd) {
        return toLocalDate(fromIntLocalDateTime(yyyyMMdd));
    }

    public static Instant fromIntInstant(Integer yyyyMMdd) {
        return toInstant(fromIntLocalDateTime(yyyyMMdd));
    }

    public static Date fromIntDate(Integer yyyyMMdd) {
        return toDate(fromIntLocalDateTime(yyyyMMdd));
    }

    public static Calendar fromIntCalendar(Integer yyyyMMdd) {
        return toCalendar(fromIntLocalDateTime(yyyyMMdd));
    }

    public static LocalDateTime fromLongLocalDateTime(Long yyyyMMddhhmmss) {
        if (yyyyMMddhhmmss == null) {
            return null;
        }
        int ss = (int) ((yyyyMMddhhmmss % 100));
        int mm = (int) ((yyyyMMddhhmmss / 100) % 100);
        int hh = (int) ((yyyyMMddhhmmss / 100_00) % 100);
        int dd = (int) ((yyyyMMddhhmmss / 100_00_00) % 100);
        int MM = (int) ((yyyyMMddhhmmss / 100_00_00_00) % 100);
        int yy = (int) ((yyyyMMddhhmmss / 100_00_00_00_00L));
        return LocalDateTime.of(yy, MM, dd, hh, mm, ss, 0);
    }

    public static LocalDate fromLongLocalDate(Long yyyyMMddhhmmss) {
        return toLocalDate(fromLongLocalDateTime(yyyyMMddhhmmss));
    }

    public static Instant fromLongInstant(Long yyyyMMddhhmmss) {
        return toInstant(fromLongLocalDateTime(yyyyMMddhhmmss));
    }

    public static Date fromLongDate(Long yyyyMMddhhmmss) {
        return toDate(fromLongLocalDateTime(yyyyMMddhhmmss));
    }

    public static Calendar fromLongCalendar(Long yyyyMMddhhmmss) {
        return toCalendar(fromLongLocalDateTime(yyyyMMddhhmmss));
    }

    //
    public static Integer toDateInt(LocalDateTime date) {
        return date == null ? null : date.getYear() * 10000 + date.getMonthValue() * 100 + date.getDayOfMonth();
    }

    public static Integer toDateInt(LocalDate date) {
        return toDateInt(toLocalDateTime(date));
    }

    public static Integer toDateInt(Instant date) {
        return toDateInt(toLocalDateTime(date));
    }

    public static Integer toDateInt(Date date) {
        return toDateInt(toLocalDateTime(date));
    }

    public static Integer toDateInt(Calendar date) {
        return toDateInt(toLocalDateTime(date));
    }

    public static Long toDateTimeLong(LocalDateTime date) {
        return date == null ? null : date.getYear() * 100_00_00_00_00L + date.getMonthValue() * 100_00_00_00L + date.getDayOfMonth() * 100_00_00L + date.getHour() * 100_00L + date.getMinute() * 100L + date.getSecond();
    }

    public static Long toDateTimeLong(LocalDate date) {
        return toDateTimeLong(toLocalDateTime(date));
    }

    public static Long toDateTimeLong(Instant date) {
        return toDateTimeLong(toLocalDateTime(date));
    }

    public static Long toDateTimeLong(Date date) {
        return toDateTimeLong(toLocalDateTime(date));
    }

    public static Long toDateTimeLong(Calendar date) {
        return toDateTimeLong(toLocalDateTime(date));
    }
    //

    /**
     * @return start of the day, such as: 2025-01-01 00:00:00.000
     */
    public static LocalDateTime dayStart(LocalDateTime date) {
        return date == null ? null : date.toLocalDate().atTime(LocalTime.MIN);
    }

    /**
     * @return start of the day, such as: 2025-01-01 00:00:00.000
     */
    public static LocalDateTime dayStart(LocalDate date) {
        return date == null ? null : date.atTime(LocalTime.MIN);
    }

    /**
     * @return start of the day, such as: 2025-01-01 00:00:00.000
     */
    public static Instant dayStart(Instant date) {
        return toInstant(dayStart(toLocalDateTime(date)));
    }

    /**
     * @return start of the day, such as: 2025-01-01 00:00:00.000
     */
    public static Date dayStart(Date date) {
        return toDate(dayStart(toLocalDateTime(date)));
    }

    /**
     * @return start of the day, such as: 2025-01-01 00:00:00.000
     */
    public static Calendar dayStart(Calendar date) {
        return toCalendar(dayStart(toLocalDateTime(date)));
    }

    /**
     * @return end of the day, such as: 2025-01-01 23:59:59.999
     */
    public static LocalDateTime dayEnd(LocalDateTime date) {
        return date == null ? null : date.toLocalDate().atTime(LocalTime.MAX);
    }

    /**
     * @return end of the day, such as: 2025-01-01 23:59:59.999
     */
    public static LocalDateTime dayEnd(LocalDate date) {
        return date == null ? null : date.atTime(LocalTime.MAX);
    }

    /**
     * @return end of the day, such as: 2025-01-01 23:59:59.999
     */
    public static Instant dayEnd(Instant date) {
        return toInstant(dayEnd(toLocalDateTime(date)));
    }

    /**
     * @return end of the day, such as: 2025-01-01 23:59:59.999
     */
    public static Date dayEnd(Date date) {
        return toDate(dayEnd(toLocalDateTime(date)));
    }

    /**
     * @return end of the day, such as: 2025-01-01 23:59:59.999
     */
    public static Calendar dayEnd(Calendar date) {
        return toCalendar(dayEnd(toLocalDateTime(date)));
    }

    //

    /**
     * @return Monday of the week, such as: Monday 00:00:00.000
     */
    public static LocalDateTime weekStart(LocalDateTime date) {
        if (date == null) {
            return null;
        }
        TemporalField dayOfWeek = WeekFields.ISO.dayOfWeek();
        return dayStart(date.with(dayOfWeek, dayOfWeek.range().getMinimum()));
    }

    /**
     * @return Monday of the week, such as: Monday 00:00:00.000
     */
    public static LocalDateTime weekStart(LocalDate date) {
        return weekStart(toLocalDateTime(date));
    }

    /**
     * @return Monday of the week, such as: Monday 00:00:00.000
     */
    public static Instant weekStart(Instant date) {
        return toInstant(weekStart(toLocalDateTime(date)));
    }

    /**
     * @return Monday of the week, such as: Monday 00:00:00.000
     */
    public static Date weekStart(Date date) {
        return toDate(weekStart(date.toInstant()));
    }

    /**
     * @return Monday of the week, such as: Monday 00:00:00.000
     */
    public static Calendar weekStart(Calendar date) {
        return toCalendar(weekStart(date.toInstant()));
    }

    /**
     * @return Sunday of the week, such as: 2025-01-05 23:59:59.999
     */
    public static LocalDateTime weekEnd(LocalDateTime date) {
        if (date == null) {
            return null;
        }
        TemporalField dayOfWeek = WeekFields.ISO.dayOfWeek();
        return dayEnd(date.with(dayOfWeek, dayOfWeek.range().getMaximum()));
    }

    /**
     * @return Sunday of the week, such as: Sunday 23:59:59.999
     */
    public static LocalDateTime weekEnd(LocalDate date) {
        return weekEnd(toLocalDateTime(date));
    }

    /**
     * @return Sunday of the week, such as: Sunday 23:59:59.999
     */
    public static Instant weekEnd(Instant date) {
        return toInstant(weekEnd(toLocalDateTime(date)));
    }

    /**
     * @return Sunday of the week, such as: Sunday 23:59:59.999
     */
    public static Date weekEnd(Date date) {
        return toDate(weekEnd(date.toInstant()));
    }

    /**
     * @return Sunday of the week, such as: Sunday 23:59:59.999
     */
    public static Calendar weekEnd(Calendar date) {
        return toCalendar(weekEnd(date.toInstant()));
    }

    /**
     * @return first day and begin time of the month, such as: 2025-01-01 00:00:00
     */
    public static LocalDateTime monthStart(LocalDateTime date) {
        return date == null ? null : dayStart(date.with(TemporalAdjusters.firstDayOfMonth()));
    }

    /**
     * @return first day and begin time of the month, such as: 2025-01-01 00:00:00
     */
    public static LocalDateTime monthStart(LocalDate date) {
        return monthStart(toLocalDateTime(date));
    }

    /**
     * @return first day and begin time of the month, such as: 2025-01-01 00:00:00
     */
    public static Instant monthStart(Instant date) {
        return toInstant(monthStart(toLocalDateTime(date)));
    }

    /**
     * @return first day and begin time of the month, such as: 2025-01-01 00:00:00
     */
    public static Date monthStart(Date date) {
        return toDate(monthStart(date.toInstant()));
    }

    /**
     * @return first day and begin time of the month, such as: 2025-01-01 00:00:00
     */
    public static Calendar monthStart(Calendar date) {
        return toCalendar(monthStart(date.toInstant()));
    }

    /**
     * @return last day and last time of the month, such as: 2025-01-31 23:59:59.999
     */
    public static LocalDateTime monthEnd(LocalDateTime date) {
        return date == null ? null : dayEnd(date.with(TemporalAdjusters.lastDayOfMonth()));
    }

    /**
     * @return last day and last time of the month, such as: 2025-01-31 23:59:59.999
     */
    public static LocalDateTime monthEnd(LocalDate date) {
        return monthEnd(toLocalDateTime(date));
    }

    /**
     * @return last day and last time of the month, such as: 2025-01-31 23:59:59.999
     */
    public static Instant monthEnd(Instant date) {
        return toInstant(monthEnd(toLocalDateTime(date)));
    }

    /**
     * @return last day and last time of the month, such as: 2025-01-31 23:59:59.999
     */
    public static Date monthEnd(Date date) {
        return toDate(monthEnd(date.toInstant()));
    }

    /**
     * @return last day and last time of the month, such as: 2025-01-31 23:59:59.999
     */
    public static Calendar monthEnd(Calendar date) {
        return toCalendar(monthEnd(date.toInstant()));
    }

    /**
     * @return first day and begin time of the year, such as: 2025-01-01 00:00:00
     */
    public static LocalDateTime yearStart(LocalDateTime date) {
        return date == null ? null : dayStart(date.with(TemporalAdjusters.firstDayOfYear()));
    }

    /**
     * @return first day and begin time of the year, such as: 2025-01-01 00:00:00
     */
    public static LocalDateTime yearStart(LocalDate date) {
        return yearStart(toLocalDateTime(date));
    }

    /**
     * @return first day and begin time of the year, such as: 2025-01-01 00:00:00
     */
    public static Instant yearStart(Instant date) {
        return toInstant(yearStart(toLocalDateTime(date)));
    }

    /**
     * @return first day and begin time of the year, such as: 2025-01-01 00:00:00
     */
    public static Date yearStart(Date date) {
        return toDate(yearStart(date.toInstant()));
    }

    /**
     * @return first day and begin time of the year, such as: 2025-01-01 00:00:00
     */
    public static Calendar yearStart(Calendar date) {
        return toCalendar(yearStart(date.toInstant()));
    }

    /**
     * @return last day and last time of the year, such as: 2025-12-31 23:59:59.999
     */
    public static LocalDateTime yearEnd(LocalDateTime date) {
        return date == null ? null : dayEnd(date.with(TemporalAdjusters.lastDayOfYear()));
    }

    /**
     * @return last day and last time of the year, such as: 2025-12-31 23:59:59.999
     */
    public static LocalDateTime yearEnd(LocalDate date) {
        return yearEnd(toLocalDateTime(date));
    }

    /**
     * @return last day and last time of the year, such as: 2025-12-31 23:59:59.999
     */
    public static Instant yearEnd(Instant date) {
        return toInstant(yearEnd(toLocalDateTime(date)));
    }

    /**
     * @return last day and last time of the year, such as: 2025-12-31 23:59:59.999
     */
    public static Date yearEnd(Date date) {
        return toDate(yearEnd(date.toInstant()));
    }

    /**
     * @return last day and last time of the year, such as: 2025-12-31 23:59:59.999
     */
    public static Calendar yearEnd(Calendar date) {
        return toCalendar(yearEnd(date.toInstant()));
    }

    //
    public static LocalDateTime plusYear(LocalDateTime date, int year) {
        return date == null ? null : date.plusYears(year);
    }

    public static LocalDate plusYear(LocalDate date, int year) {
        return date == null ? null : date.plusYears(year);
    }

    public static Instant plusYear(Instant date, int year) {
        return toInstant(plusYear(toLocalDateTime(date), year));
    }

    public static Date plusYear(Date date, int year) {
        return toDate(plusYear(toLocalDateTime(date), year));
    }

    public static Calendar plusYear(Calendar date, int year) {
        return toCalendar(plusYear(toLocalDateTime(date), year));
    }

    public static LocalDateTime plusMonth(LocalDateTime date, int month) {
        return date == null ? null : date.plusMonths(month);
    }

    public static LocalDate plusMonth(LocalDate date, int month) {
        return date == null ? null : date.plusMonths(month);
    }

    public static Instant plusMonth(Instant date, int month) {
        return toInstant(plusMonth(toLocalDateTime(date), month));
    }

    public static Date plusMonth(Date date, int month) {
        return toDate(plusMonth(toLocalDateTime(date), month));
    }

    public static Calendar plusMonth(Calendar date, int month) {
        return toCalendar(plusMonth(toLocalDateTime(date), month));
    }


    public static LocalDateTime plusWeek(LocalDateTime date, int week) {
        return date == null ? null : date.plusWeeks(week);
    }

    public static LocalDate plusWeek(LocalDate date, int week) {
        return date == null ? null : date.plusWeeks(week);
    }

    public static Instant plusWeek(Instant date, int week) {
        return toInstant(plusWeek(toLocalDateTime(date), week));
    }

    public static Date plusWeek(Date date, int week) {
        return toDate(plusWeek(toLocalDateTime(date), week));
    }

    public static Calendar plusWeek(Calendar date, int week) {
        return toCalendar(plusWeek(toLocalDateTime(date), week));
    }

    public static LocalDateTime plusDay(LocalDateTime date, int day) {
        return date == null ? null : date.plusDays(day);
    }

    public static LocalDate plusDay(LocalDate date, int day) {
        return date == null ? null : date.plusDays(day);
    }

    public static Instant plusDay(Instant date, int day) {
        return toInstant(plusDay(toLocalDateTime(date), day));
    }

    public static Date plusDay(Date date, int day) {
        return toDate(plusDay(toLocalDateTime(date), day));
    }

    public static Calendar plusDay(Calendar date, int day) {
        return toCalendar(plusDay(toLocalDateTime(date), day));
    }

    public static LocalDateTime plusHour(LocalDateTime date, int hour) {
        return date == null ? null : date.plusHours(hour);
    }

    public static LocalDateTime plusHour(LocalDate date, int hour) {
        return plusHour(toLocalDateTime(date), hour);
    }

    public static Instant plusHour(Instant date, int hour) {
        return toInstant(plusHour(toLocalDateTime(date), hour));
    }

    public static Date plusHour(Date date, int hour) {
        return toDate(plusHour(toLocalDateTime(date), hour));
    }

    public static Calendar plusHour(Calendar date, int hour) {
        return toCalendar(plusHour(toLocalDateTime(date), hour));
    }

    public static LocalDateTime plusMinute(LocalDateTime date, int minute) {
        return date == null ? null : date.plusMinutes(minute);
    }

    public static LocalDateTime plusMinute(LocalDate date, int minute) {
        return plusMinute(toLocalDateTime(date), minute);
    }

    public static Instant plusMinute(Instant date, int minute) {
        return toInstant(plusMinute(toLocalDateTime(date), minute));
    }

    public static Date plusMinute(Date date, int minute) {
        return toDate(plusMinute(toLocalDateTime(date), minute));
    }

    public static Calendar plusMinute(Calendar date, int minute) {
        return toCalendar(plusMinute(toLocalDateTime(date), minute));
    }

    public static LocalDateTime plusSecond(LocalDateTime date, int second) {
        return date == null ? null : date.plusSeconds(second);
    }

    public static LocalDateTime plusSecond(LocalDate date, int second) {
        return plusSecond(toLocalDateTime(date), second);
    }

    public static Instant plusSecond(Instant date, int second) {
        return toInstant(plusSecond(toLocalDateTime(date), second));
    }

    public static Date plusSecond(Date date, int second) {
        return toDate(plusSecond(toLocalDateTime(date), second));
    }

    public static Calendar plusSecond(Calendar date, int second) {
        return toCalendar(plusSecond(toLocalDateTime(date), second));
    }

    public static LocalDateTime plusMillisecond(LocalDateTime date, int millisecond) {
        return date == null ? null : date.plusNanos(millisecond * 1_000_000);
    }

    public static LocalDateTime plusMillisecond(LocalDate date, int millisecond) {
        return plusMillisecond(toLocalDateTime(date), millisecond);
    }

    public static Instant plusMillisecond(Instant date, int millisecond) {
        return toInstant(plusMillisecond(toLocalDateTime(date), millisecond));
    }

    public static Date plusMillisecond(Date date, int millisecond) {
        return toDate(plusMillisecond(toLocalDateTime(date), millisecond));
    }

    public static Calendar plusMillisecond(Calendar date, int millisecond) {
        return toCalendar(plusMillisecond(toLocalDateTime(date), millisecond));
    }

    //

    /**
     * @return start millisecond of the second, such as:2025-02-02 02:02:02.000
     */
    public static LocalDateTime truncatedToSecond(LocalDateTime date) {
        return date == null ? null : date.withNano(0);
    }

    /**
     * @return start millisecond of the second, such as:2025-02-02 02:02:02.000
     */
    public static LocalDate truncatedToSecond(LocalDate date) {
        return date;
    }

    /**
     * @return start millisecond of the second, such as:2025-02-02 02:02:02.000
     */
    public static Instant truncatedToSecond(Instant date) {
        return toInstant(truncatedToSecond(toLocalDateTime(date)));
    }

    /**
     * @return start millisecond of the second, such as:2025-02-02 02:02:02.000
     */
    public static Date truncatedToSecond(Date date) {
        return toDate(truncatedToSecond(toLocalDateTime(date)));
    }

    /**
     * @return start millisecond of the second, such as:2025-02-02 02:02:02.000
     */
    public static Calendar truncatedToSecond(Calendar date) {
        return toCalendar(truncatedToSecond(toLocalDateTime(date)));
    }

    /**
     * @return start second of the minute, such as:2025-02-02 02:02:00
     */
    public static LocalDateTime truncatedToMinute(LocalDateTime date) {
        return date == null ? null : date.truncatedTo(ChronoUnit.MINUTES);
    }

    /**
     * @return start second of the minute, such as:2025-02-02 02:02:00
     */
    public static LocalDate truncatedToMinute(LocalDate date) {
        return date;
    }

    /**
     * @return start second of the minute, such as:2025-02-02 02:02:00
     */
    public static Instant truncatedToMinute(Instant date) {
        return toInstant(truncatedToMinute(toLocalDateTime(date)));
    }

    /**
     * @return start second of the minute, such as:2025-02-02 02:02:00
     */
    public static Date truncatedToMinute(Date date) {
        return toDate(truncatedToMinute(toLocalDateTime(date)));
    }

    /**
     * @return start second of the minute, such as:2025-02-02 02:02:00
     */
    public static Calendar truncatedToMinute(Calendar date) {
        return toCalendar(truncatedToMinute(toLocalDateTime(date)));
    }

    /**
     * @return start minute of the hour, such as:2025-02-02 02:00:00
     */
    public static LocalDateTime truncatedToHour(LocalDateTime date) {
        return date == null ? null : date.truncatedTo(ChronoUnit.HOURS);
    }

    /**
     * @return start minute of the hour, such as:2025-02-02 02:00:00
     */
    public static LocalDate truncatedToHour(LocalDate date) {
        return date;
    }

    /**
     * @return start minute of the hour, such as:2025-02-02 02:00:00
     */
    public static Instant truncatedToHour(Instant date) {
        return toInstant(truncatedToHour(toLocalDateTime(date)));
    }

    /**
     * @return start minute of the hour, such as:2025-02-02 02:00:00
     */
    public static Date truncatedToHour(Date date) {
        return toDate(truncatedToHour(toLocalDateTime(date)));
    }

    /**
     * @return start minute of the hour, such as:2025-02-02 02:00:00
     */
    public static Calendar truncatedToHour(Calendar date) {
        return toCalendar(truncatedToHour(toLocalDateTime(date)));
    }

    /**
     * @return start time of the day, such as:2025-02-02 00:00:00
     */
    public static LocalDateTime truncatedToDay(LocalDateTime date) {
        return dayStart(date);
    }

    /**
     * @return start time of the day, such as:2025-02-02 00:00:00
     */
    public static LocalDate truncatedToDay(LocalDate date) {
        return toLocalDate(dayStart(date));
    }

    /**
     * @return start time of the day, such as:2025-02-02 00:00:00
     */
    public static Instant truncatedToDay(Instant date) {
        return dayStart(date);
    }

    /**
     * @return start time of the day, such as:2025-02-02 00:00:00
     */
    public static Date truncatedToDay(Date date) {
        return dayStart(date);
    }

    /**
     * @return start of the day, such as:2025-02-02 00:00:00
     */
    public static Calendar truncatedToDay(Calendar date) {
        return dayStart(date);
    }

    /**
     * @return Monday of the week, such as:Monday 00:00:00
     */
    public static LocalDateTime truncatedToWeek(LocalDateTime date) {
        return weekStart(date);
    }

    /**
     * @return Monday of the week, such as:Monday 00:00:00
     */
    public static LocalDate truncatedToWeek(LocalDate date) {
        return toLocalDate(weekStart(date));
    }

    /**
     * @return Monday of the week, such as:Monday 00:00:00
     */
    public static Instant truncatedToWeek(Instant date) {
        return weekStart(date);
    }

    /**
     * @return Monday of the week, such as:Monday 00:00:00
     */
    public static Date truncatedToWeek(Date date) {
        return weekStart(date);
    }

    /**
     * @return Monday of the week, such as:Monday 00:00:00
     */
    public static Calendar truncatedToWeek(Calendar date) {
        return weekStart(date);
    }

    /**
     * @return the first day of the month, such as:2025-02-01 00:00:00
     */
    public static LocalDateTime truncatedToMonth(LocalDateTime date) {
        return monthStart(date);
    }

    /**
     * @return the first day of the month, such as:2025-02-01 00:00:00
     */
    public static LocalDate truncatedToMonth(LocalDate date) {
        return toLocalDate(monthStart(date));
    }

    /**
     * @return the first day of the month, such as:2025-02-01 00:00:00
     */
    public static Instant truncatedToMonth(Instant date) {
        return monthStart(date);
    }

    /**
     * @return the first day of the month, such as:2025-02-01 00:00:00
     */
    public static Date truncatedToMonth(Date date) {
        return monthStart(date);
    }

    /**
     * @return the first day of the month, such as:2025-02-01 00:00:00
     */
    public static Calendar truncatedToMonth(Calendar date) {
        return monthStart(date);
    }

    /**
     * @return the first day of the year, such as:2025-01-01 00:00:00
     */
    public static LocalDateTime truncatedToYear(LocalDateTime date) {
        return yearStart(date);
    }

    /**
     * @return the first day of the year, such as:2025-01-01 00:00:00
     */
    public static LocalDate truncatedToYear(LocalDate date) {
        return toLocalDate(yearStart(date));
    }

    /**
     * @return the first day of the year, such as:2025-01-01 00:00:00
     */
    public static Instant truncatedToYear(Instant date) {
        return yearStart(date);
    }

    /**
     * @return the first day of the year, such as:2025-01-01 00:00:00
     */
    public static Date truncatedToYear(Date date) {
        return yearStart(date);
    }

    /**
     * @return the first day of the year, such as:2025-01-01 00:00:00
     */
    public static Calendar truncatedToYear(Calendar date) {
        return yearStart(date);
    }

    /**
     * set nano
     */
    public static LocalDateTime setNano(LocalDateTime date, int nano) {
        return date == null ? null : date.withNano(nano);
    }

    /**
     * set nano
     */
    public static LocalDateTime setNano(LocalDate date, int nano) {
        return setNano(toLocalDateTime(date), nano);
    }

    /**
     * set nano
     */
    public static Instant setNano(Instant date, int nano) {
        return toInstant(setNano(toLocalDateTime(date), nano));
    }

    /**
     * set nano
     */
    public static LocalDateTime setNano(Date date, int nano) {
        return setNano(toLocalDateTime(date), nano);
    }

    /**
     * set nano
     */
    public static LocalDateTime setNano(Calendar date, int nano) {
        return setNano(toLocalDateTime(date), nano);
    }

    /**
     * set millisecond
     */
    public static LocalDateTime setMillisecond(LocalDateTime date, int millisecond) {
        return date == null ? null : date.withNano(millisecond * 1_000_000);
    }

    /**
     * set millisecond
     */
    public static LocalDateTime setMillisecond(LocalDate date, int millisecond) {
        return setMillisecond(toLocalDateTime(date), millisecond);
    }

    /**
     * set millisecond
     */
    public static Instant setMillisecond(Instant date, int millisecond) {
        return toInstant(setMillisecond(toLocalDateTime(date), millisecond));
    }

    /**
     * set millisecond
     */
    public static Date setMillisecond(Date date, int millisecond) {
        return toDate(setMillisecond(toLocalDateTime(date), millisecond));
    }

    /**
     * set millisecond
     */
    public static Calendar setMillisecond(Calendar date, int millisecond) {
        return toCalendar(setMillisecond(toLocalDateTime(date), millisecond));
    }

    /**
     * set second
     */
    public static LocalDateTime setSecond(LocalDateTime date, int second) {
        return date == null ? null : date.withSecond(second);
    }

    /**
     * set second
     */
    public static LocalDateTime setSecond(LocalDate date, int second) {
        return setSecond(toLocalDateTime(date), second);
    }

    /**
     * set second
     */
    public static Instant setSecond(Instant date, int second) {
        return toInstant(setSecond(toLocalDateTime(date), second));
    }

    /**
     * set second
     */
    public static Date setSecond(Date date, int second) {
        return toDate(setSecond(toLocalDateTime(date), second));
    }

    /**
     * set second
     */
    public static Calendar setSecond(Calendar date, int second) {
        return toCalendar(setSecond(toLocalDateTime(date), second));
    }

    /**
     * set minute
     */
    public static LocalDateTime setMinute(LocalDateTime date, int minute) {
        return date == null ? null : date.withMinute(minute);
    }

    /**
     * set minute
     */
    public static LocalDateTime setMinute(LocalDate date, int minute) {
        return setMinute(toLocalDateTime(date), minute);
    }

    /**
     * set minute
     */
    public static Instant setMinute(Instant date, int minute) {
        return toInstant(setMinute(toLocalDateTime(date), minute));
    }

    /**
     * set minute
     */
    public static Date setMinute(Date date, int minute) {
        return toDate(setMinute(toLocalDateTime(date), minute));
    }

    /**
     * set minute
     */
    public static Calendar setMinute(Calendar date, int minute) {
        return toCalendar(setMinute(toLocalDateTime(date), minute));
    }

    /**
     * set hour
     */
    public static LocalDateTime setHour(LocalDateTime date, int hour) {
        return date == null ? null : date.withHour(hour);
    }

    /**
     * set hour
     */
    public static LocalDateTime setHour(LocalDate date, int hour) {
        return setHour(toLocalDateTime(date), hour);
    }

    /**
     * set hour
     */
    public static Instant setHour(Instant date, int hour) {
        return toInstant(setHour(toLocalDateTime(date), hour));
    }

    /**
     * set hour
     */
    public static Date setHour(Date date, int hour) {
        return toDate(setHour(toLocalDateTime(date), hour));
    }

    /**
     * set hour
     */
    public static Calendar setHour(Calendar date, int hour) {
        return toCalendar(setHour(toLocalDateTime(date), hour));
    }

    /**
     * set day
     */
    public static LocalDateTime setDay(LocalDateTime date, int day) {
        return date == null ? null : date.withDayOfMonth(day);
    }

    /**
     * set day
     */
    public static LocalDate setDay(LocalDate date, int day) {
        return date == null ? null : date.withDayOfMonth(day);
    }

    /**
     * set day
     */
    public static Instant setDay(Instant date, int day) {
        return toInstant(setDay(toLocalDateTime(date), day));
    }

    /**
     * set day
     */
    public static Date setDay(Date date, int day) {
        return toDate(setDay(toLocalDateTime(date), day));
    }

    /**
     * set day
     */
    public static Calendar setDay(Calendar date, int day) {
        return toCalendar(setDay(toLocalDateTime(date), day));
    }

    /**
     * set month: from 1 (January) to 12 (December)
     */
    public static LocalDateTime setMonth(LocalDateTime date, int month) {
        return date == null ? null : date.withMonth(month);
    }

    /**
     * set month: from 1 (January) to 12 (December)
     */
    public static LocalDate setMonth(LocalDate date, int month) {
        return date == null ? null : date.withMonth(month);
    }

    /**
     * set month: from 1 (January) to 12 (December)
     */
    public static Instant setMonth(Instant date, int month) {
        return toInstant(setMonth(toLocalDateTime(date), month));
    }

    /**
     * set month: from 1 (January) to 12 (December)
     */
    public static Date setMonth(Date date, int month) {
        return toDate(setMonth(toLocalDateTime(date), month));
    }

    /**
     * set month: from 1 (January) to 12 (December)
     */
    public static Calendar setMonth(Calendar date, int month) {
        return toCalendar(setMonth(toLocalDateTime(date), month));
    }

    /**
     * set year
     */
    public static LocalDateTime setYear(LocalDateTime date, int year) {
        return date == null ? null : date.withYear(year);
    }

    /**
     * set year
     */
    public static LocalDate setYear(LocalDate date, int year) {
        return date == null ? null : date.withYear(year);
    }

    /**
     * set year
     */
    public static Instant setYear(Instant date, int year) {
        return toInstant(setYear(toLocalDateTime(date), year));
    }

    /**
     * set year
     */
    public static Date setYear(Date date, int year) {
        return toDate(setYear(toLocalDateTime(date), year));
    }

    /**
     * set year
     */
    public static Calendar setYear(Calendar date, int year) {
        return toCalendar(setYear(toLocalDateTime(date), year));
    }

    /**
     * Obtains an instance of {@code LocalDateTime} from year, month, day, hour, minute, second and nanosecond.
     *
     * @param year         the year to represent, from MIN_YEAR to MAX_YEAR
     * @param month        the month-of-year to represent, from 1 (January) to 12 (December)
     * @param dayOfMonth   the day-of-month to represent, from 1 to 31
     * @param hour         the hour-of-day to represent, from 0 to 23
     * @param minute       the minute-of-hour to represent, from 0 to 59
     * @param second       the second-of-minute to represent, from 0 to 59
     * @param nanoOfSecond the nano-of-second to represent, from 0 to 999,999,999
     * @return the local date-time, not null
     * @throws DateTimeException if the value of any field is out of range, or if the day-of-month is invalid for the month-year
     */
    public static LocalDateTime of(int year, int month, int dayOfMonth, int hour, int minute, int second, int nanoOfSecond) {
        return LocalDateTime.of(year, month, dayOfMonth, hour, minute, second, nanoOfSecond);
    }

    /**
     * Obtains an instance of {@code LocalDateTime} from year, month, day, hour, minute, second and nanosecond.
     *
     * @param year       the year to represent, from MIN_YEAR to MAX_YEAR
     * @param month      the month-of-year to represent, from 1 (January) to 12 (December)
     * @param dayOfMonth the day-of-month to represent, from 1 to 31
     * @param hour       the hour-of-day to represent, from 0 to 23
     * @param minute     the minute-of-hour to represent, from 0 to 59
     * @param second     the second-of-minute to represent, from 0 to 59
     * @return the local date-time, not null
     * @throws DateTimeException if the value of any field is out of range, or if the day-of-month is invalid for the month-year
     */
    public static LocalDateTime of(int year, int month, int dayOfMonth, int hour, int minute, int second) {
        return LocalDateTime.of(year, month, dayOfMonth, hour, minute, second, 0);
    }

    /**
     * Obtains an instance of {@code LocalDateTime} from year, month, day, hour, minute, second and nanosecond.
     *
     * @param year       the year to represent, from MIN_YEAR to MAX_YEAR
     * @param month      the month-of-year to represent, from 1 (January) to 12 (December)
     * @param dayOfMonth the day-of-month to represent, from 1 to 31
     * @param hour       the hour-of-day to represent, from 0 to 23
     * @param minute     the minute-of-hour to represent, from 0 to 59
     * @return the local date-time, not null
     * @throws DateTimeException if the value of any field is out of range, or if the day-of-month is invalid for the month-year
     */
    public static LocalDateTime of(int year, int month, int dayOfMonth, int hour, int minute) {
        return LocalDateTime.of(year, month, dayOfMonth, hour, minute, 0, 0);
    }

    /**
     * Obtains an instance of {@code LocalDateTime} from year, month, day, hour, minute, second and nanosecond.
     *
     * @param year       the year to represent, from MIN_YEAR to MAX_YEAR
     * @param month      the month-of-year to represent, from 1 (January) to 12 (December)
     * @param dayOfMonth the day-of-month to represent, from 1 to 31
     * @param hour       the hour-of-day to represent, from 0 to 23
     * @return the local date-time, not null
     * @throws DateTimeException if the value of any field is out of range, or if the day-of-month is invalid for the month-year
     */
    public static LocalDateTime of(int year, int month, int dayOfMonth, int hour) {
        return LocalDateTime.of(year, month, dayOfMonth, hour, 0, 0, 0);
    }

    /**
     * Obtains an instance of {@code LocalDateTime} from year, month, day, hour, minute, second and nanosecond.
     *
     * @param year       the year to represent, from MIN_YEAR to MAX_YEAR
     * @param month      the month-of-year to represent, from 1 (January) to 12 (December)
     * @param dayOfMonth the day-of-month to represent, from 1 to 31
     * @return the local date-time, not null
     * @throws DateTimeException if the value of any field is out of range, or if the day-of-month is invalid for the month-year
     */
    public static LocalDateTime of(int year, int month, int dayOfMonth) {
        return LocalDateTime.of(year, month, dayOfMonth, 0, 0, 0, 0);
    }

    /**
     * Obtains an instance of {@code LocalDate} from year, month, day, hour, minute, second and nanosecond.
     *
     * @param year         the year to represent, from MIN_YEAR to MAX_YEAR
     * @param month        the month-of-year to represent, from 1 (January) to 12 (December)
     * @param dayOfMonth   the day-of-month to represent, from 1 to 31
     * @param hour         the hour-of-day to represent, from 0 to 23
     * @param minute       the minute-of-hour to represent, from 0 to 59
     * @param second       the second-of-minute to represent, from 0 to 59
     * @param nanoOfSecond the nano-of-second to represent, from 0 to 999,999,999
     * @return the local date-time, not null
     * @throws DateTimeException if the value of any field is out of range, or if the day-of-month is invalid for the month-year
     */
    public static LocalDate ofLocalDate(int year, int month, int dayOfMonth, int hour, int minute, int second, int nanoOfSecond) {
        return LocalDate.of(year, month, dayOfMonth);
    }

    /**
     * Obtains an instance of {@code LocalDate} from year, month, day, hour, minute, second and nanosecond.
     *
     * @param year       the year to represent, from MIN_YEAR to MAX_YEAR
     * @param month      the month-of-year to represent, from 1 (January) to 12 (December)
     * @param dayOfMonth the day-of-month to represent, from 1 to 31
     * @param hour       the hour-of-day to represent, from 0 to 23
     * @param minute     the minute-of-hour to represent, from 0 to 59
     * @param second     the second-of-minute to represent, from 0 to 59
     * @return the local date-time, not null
     * @throws DateTimeException if the value of any field is out of range, or if the day-of-month is invalid for the month-year
     */
    public static LocalDate ofLocalDate(int year, int month, int dayOfMonth, int hour, int minute, int second) {
        return LocalDate.of(year, month, dayOfMonth);
    }

    /**
     * Obtains an instance of {@code LocalDate} from year, month, day, hour, minute, second and nanosecond.
     *
     * @param year       the year to represent, from MIN_YEAR to MAX_YEAR
     * @param month      the month-of-year to represent, from 1 (January) to 12 (December)
     * @param dayOfMonth the day-of-month to represent, from 1 to 31
     * @param hour       the hour-of-day to represent, from 0 to 23
     * @param minute     the minute-of-hour to represent, from 0 to 59
     * @return the local date-time, not null
     * @throws DateTimeException if the value of any field is out of range, or if the day-of-month is invalid for the month-year
     */
    public static LocalDate ofLocalDate(int year, int month, int dayOfMonth, int hour, int minute) {
        return LocalDate.of(year, month, dayOfMonth);
    }

    /**
     * Obtains an instance of {@code LocalDate} from year, month, day, hour, minute, second and nanosecond.
     *
     * @param year       the year to represent, from MIN_YEAR to MAX_YEAR
     * @param month      the month-of-year to represent, from 1 (January) to 12 (December)
     * @param dayOfMonth the day-of-month to represent, from 1 to 31
     * @param hour       the hour-of-day to represent, from 0 to 23
     * @return the local date-time, not null
     * @throws DateTimeException if the value of any field is out of range, or if the day-of-month is invalid for the month-year
     */
    public static LocalDate ofLocalDate(int year, int month, int dayOfMonth, int hour) {
        return LocalDate.of(year, month, dayOfMonth);
    }

    /**
     * Obtains an instance of {@code LocalDate} from year, month, day, hour, minute, second and nanosecond.
     *
     * @param year       the year to represent, from MIN_YEAR to MAX_YEAR
     * @param month      the month-of-year to represent, from 1 (January) to 12 (December)
     * @param dayOfMonth the day-of-month to represent, from 1 to 31
     * @return the local date-time, not null
     * @throws DateTimeException if the value of any field is out of range, or if the day-of-month is invalid for the month-year
     */
    public static LocalDate ofLocalDate(int year, int month, int dayOfMonth) {
        return LocalDate.of(year, month, dayOfMonth);
    }

    /**
     * Obtains an instance of {@code Instant} from year, month, day, hour, minute, second and nanosecond.
     *
     * @param year         the year to represent, from MIN_YEAR to MAX_YEAR
     * @param month        the month-of-year to represent, from 1 (January) to 12 (December)
     * @param dayOfMonth   the day-of-month to represent, from 1 to 31
     * @param hour         the hour-of-day to represent, from 0 to 23
     * @param minute       the minute-of-hour to represent, from 0 to 59
     * @param second       the second-of-minute to represent, from 0 to 59
     * @param nanoOfSecond the nano-of-second to represent, from 0 to 999,999,999
     * @return the local date-time, not null
     * @throws DateTimeException if the value of any field is out of range, or if the day-of-month is invalid for the month-year
     */
    public static Instant ofInstant(int year, int month, int dayOfMonth, int hour, int minute, int second, int nanoOfSecond) {
        return toInstant(of(year, month, dayOfMonth, hour, minute, second, nanoOfSecond));
    }

    /**
     * Obtains an instance of {@code Instant} from year, month, day, hour, minute, second and nanosecond.
     *
     * @param year       the year to represent, from MIN_YEAR to MAX_YEAR
     * @param month      the month-of-year to represent, from 1 (January) to 12 (December)
     * @param dayOfMonth the day-of-month to represent, from 1 to 31
     * @param hour       the hour-of-day to represent, from 0 to 23
     * @param minute     the minute-of-hour to represent, from 0 to 59
     * @param second     the second-of-minute to represent, from 0 to 59
     * @return the local date-time, not null
     * @throws DateTimeException if the value of any field is out of range, or if the day-of-month is invalid for the month-year
     */
    public static Instant ofInstant(int year, int month, int dayOfMonth, int hour, int minute, int second) {
        return toInstant(of(year, month, dayOfMonth, hour, minute, second));
    }

    /**
     * Obtains an instance of {@code Instant} from year, month, day, hour, minute, second and nanosecond.
     *
     * @param year       the year to represent, from MIN_YEAR to MAX_YEAR
     * @param month      the month-of-year to represent, from 1 (January) to 12 (December)
     * @param dayOfMonth the day-of-month to represent, from 1 to 31
     * @param hour       the hour-of-day to represent, from 0 to 23
     * @param minute     the minute-of-hour to represent, from 0 to 59
     * @return the local date-time, not null
     * @throws DateTimeException if the value of any field is out of range, or if the day-of-month is invalid for the month-year
     */
    public static Instant ofInstant(int year, int month, int dayOfMonth, int hour, int minute) {
        return toInstant(of(year, month, dayOfMonth, hour, minute));
    }

    /**
     * Obtains an instance of {@code Instant} from year, month, day, hour, minute, second and nanosecond.
     *
     * @param year       the year to represent, from MIN_YEAR to MAX_YEAR
     * @param month      the month-of-year to represent, from 1 (January) to 12 (December)
     * @param dayOfMonth the day-of-month to represent, from 1 to 31
     * @param hour       the hour-of-day to represent, from 0 to 23
     * @return the local date-time, not null
     * @throws DateTimeException if the value of any field is out of range, or if the day-of-month is invalid for the month-year
     */
    public static Instant ofInstant(int year, int month, int dayOfMonth, int hour) {
        return toInstant(of(year, month, dayOfMonth, hour));
    }

    /**
     * Obtains an instance of {@code Instant} from year, month, day, hour, minute, second and nanosecond.
     *
     * @param year       the year to represent, from MIN_YEAR to MAX_YEAR
     * @param month      the month-of-year to represent, from 1 (January) to 12 (December)
     * @param dayOfMonth the day-of-month to represent, from 1 to 31
     * @return the local date-time, not null
     * @throws DateTimeException if the value of any field is out of range, or if the day-of-month is invalid for the month-year
     */
    public static Instant ofInstant(int year, int month, int dayOfMonth) {
        return toInstant(of(year, month, dayOfMonth));
    }

    /**
     * Obtains an instance of {@code Date} from year, month, day, hour, minute, second and nanosecond.
     *
     * @param year         the year to represent, from MIN_YEAR to MAX_YEAR
     * @param month        the month-of-year to represent, from 1 (January) to 12 (December)
     * @param dayOfMonth   the day-of-month to represent, from 1 to 31
     * @param hour         the hour-of-day to represent, from 0 to 23
     * @param minute       the minute-of-hour to represent, from 0 to 59
     * @param second       the second-of-minute to represent, from 0 to 59
     * @param nanoOfSecond the nano-of-second to represent, from 0 to 999,999,999
     * @return the local date-time, not null
     * @throws DateTimeException if the value of any field is out of range, or if the day-of-month is invalid for the month-year
     */
    public static Date ofDate(int year, int month, int dayOfMonth, int hour, int minute, int second, int nanoOfSecond) {
        return toDate(of(year, month, dayOfMonth, hour, minute, second, nanoOfSecond));
    }

    /**
     * Obtains an instance of {@code Date} from year, month, day, hour, minute, second and nanosecond.
     *
     * @param year       the year to represent, from MIN_YEAR to MAX_YEAR
     * @param month      the month-of-year to represent, from 1 (January) to 12 (December)
     * @param dayOfMonth the day-of-month to represent, from 1 to 31
     * @param hour       the hour-of-day to represent, from 0 to 23
     * @param minute     the minute-of-hour to represent, from 0 to 59
     * @param second     the second-of-minute to represent, from 0 to 59
     * @return the local date-time, not null
     * @throws DateTimeException if the value of any field is out of range, or if the day-of-month is invalid for the month-year
     */
    public static Date ofDate(int year, int month, int dayOfMonth, int hour, int minute, int second) {
        return toDate(of(year, month, dayOfMonth, hour, minute, second));
    }

    /**
     * Obtains an instance of {@code Date} from year, month, day, hour, minute, second and nanosecond.
     *
     * @param year       the year to represent, from MIN_YEAR to MAX_YEAR
     * @param month      the month-of-year to represent, from 1 (January) to 12 (December)
     * @param dayOfMonth the day-of-month to represent, from 1 to 31
     * @param hour       the hour-of-day to represent, from 0 to 23
     * @param minute     the minute-of-hour to represent, from 0 to 59
     * @return the local date-time, not null
     * @throws DateTimeException if the value of any field is out of range, or if the day-of-month is invalid for the month-year
     */
    public static Date ofDate(int year, int month, int dayOfMonth, int hour, int minute) {
        return toDate(of(year, month, dayOfMonth, hour, minute));
    }

    /**
     * Obtains an instance of {@code Date} from year, month, day, hour, minute, second and nanosecond.
     *
     * @param year       the year to represent, from MIN_YEAR to MAX_YEAR
     * @param month      the month-of-year to represent, from 1 (January) to 12 (December)
     * @param dayOfMonth the day-of-month to represent, from 1 to 31
     * @param hour       the hour-of-day to represent, from 0 to 23
     * @return the local date-time, not null
     * @throws DateTimeException if the value of any field is out of range, or if the day-of-month is invalid for the month-year
     */
    public static Date ofDate(int year, int month, int dayOfMonth, int hour) {
        return toDate(of(year, month, dayOfMonth, hour));
    }

    /**
     * Obtains an instance of {@code Date} from year, month, day, hour, minute, second and nanosecond.
     *
     * @param year       the year to represent, from MIN_YEAR to MAX_YEAR
     * @param month      the month-of-year to represent, from 1 (January) to 12 (December)
     * @param dayOfMonth the day-of-month to represent, from 1 to 31
     * @return the local date-time, not null
     * @throws DateTimeException if the value of any field is out of range, or if the day-of-month is invalid for the month-year
     */
    public static Date ofDate(int year, int month, int dayOfMonth) {
        return toDate(of(year, month, dayOfMonth));
    }

    /**
     * Obtains an instance of {@code Calendar} from year, month, day, hour, minute, second and nanosecond.
     *
     * @param year         the year to represent, from MIN_YEAR to MAX_YEAR
     * @param month        the month-of-year to represent, from 1 (January) to 12 (December)
     * @param dayOfMonth   the day-of-month to represent, from 1 to 31
     * @param hour         the hour-of-day to represent, from 0 to 23
     * @param minute       the minute-of-hour to represent, from 0 to 59
     * @param second       the second-of-minute to represent, from 0 to 59
     * @param nanoOfSecond the nano-of-second to represent, from 0 to 999,999,999
     * @return the local date-time, not null
     * @throws DateTimeException if the value of any field is out of range, or if the day-of-month is invalid for the month-year
     */
    public static Calendar ofCalendar(int year, int month, int dayOfMonth, int hour, int minute, int second, int nanoOfSecond) {
        return toCalendar(of(year, month, dayOfMonth, hour, minute, second, nanoOfSecond));
    }

    /**
     * Obtains an instance of {@code Calendar} from year, month, day, hour, minute, second and nanosecond.
     *
     * @param year       the year to represent, from MIN_YEAR to MAX_YEAR
     * @param month      the month-of-year to represent, from 1 (January) to 12 (December)
     * @param dayOfMonth the day-of-month to represent, from 1 to 31
     * @param hour       the hour-of-day to represent, from 0 to 23
     * @param minute     the minute-of-hour to represent, from 0 to 59
     * @param second     the second-of-minute to represent, from 0 to 59
     * @return the local date-time, not null
     * @throws DateTimeException if the value of any field is out of range, or if the day-of-month is invalid for the month-year
     */
    public static Calendar ofCalendar(int year, int month, int dayOfMonth, int hour, int minute, int second) {
        return toCalendar(of(year, month, dayOfMonth, hour, minute, second));
    }

    /**
     * Obtains an instance of {@code Calendar} from year, month, day, hour, minute, second and nanosecond.
     *
     * @param year       the year to represent, from MIN_YEAR to MAX_YEAR
     * @param month      the month-of-year to represent, from 1 (January) to 12 (December)
     * @param dayOfMonth the day-of-month to represent, from 1 to 31
     * @param hour       the hour-of-day to represent, from 0 to 23
     * @param minute     the minute-of-hour to represent, from 0 to 59
     * @return the local date-time, not null
     * @throws DateTimeException if the value of any field is out of range, or if the day-of-month is invalid for the month-year
     */
    public static Calendar ofCalendar(int year, int month, int dayOfMonth, int hour, int minute) {
        return toCalendar(of(year, month, dayOfMonth, hour, minute));
    }

    /**
     * Obtains an instance of {@code Calendar} from year, month, day, hour, minute, second and nanosecond.
     *
     * @param year       the year to represent, from MIN_YEAR to MAX_YEAR
     * @param month      the month-of-year to represent, from 1 (January) to 12 (December)
     * @param dayOfMonth the day-of-month to represent, from 1 to 31
     * @param hour       the hour-of-day to represent, from 0 to 23
     * @return the local date-time, not null
     * @throws DateTimeException if the value of any field is out of range, or if the day-of-month is invalid for the month-year
     */
    public static Calendar ofCalendar(int year, int month, int dayOfMonth, int hour) {
        return toCalendar(of(year, month, dayOfMonth, hour));
    }

    /**
     * Obtains an instance of {@code Calendar} from year, month, day, hour, minute, second and nanosecond.
     *
     * @param year       the year to represent, from MIN_YEAR to MAX_YEAR
     * @param month      the month-of-year to represent, from 1 (January) to 12 (December)
     * @param dayOfMonth the day-of-month to represent, from 1 to 31
     * @return the local date-time, not null
     * @throws DateTimeException if the value of any field is out of range, or if the day-of-month is invalid for the month-year
     */
    public static Calendar ofCalendar(int year, int month, int dayOfMonth) {
        return toCalendar(of(year, month, dayOfMonth));
    }

    /**
     * LocalDateTime subtract, return the millisecond.
     * if LocalDateTime is null set millisecond to 0.
     *
     * @param d0 if null set millisecond to 0
     * @param d1 if null set millisecond to 0
     * @return d0-d1=millisecond
     */
    public static long subtract(LocalDateTime d0, LocalDateTime d1) {
        long start = 0L, end = 0L;
        if (d0 != null) {
            start = getTime(d0);
        }
        if (d1 != null) {
            end = getTime(d1);
        }
        return start - end;
    }


    /**
     * LocalDateTime subtract, return the nanosecond.
     * if LocalDateTime is null set nanosecond to 0.
     *
     * @param d0 if null set nanosecond to 0
     * @param d1 if null set nanosecond to 0
     * @return d0-d1=nanosecond
     */
    public static long subtractNano(LocalDateTime d0, LocalDateTime d1) {
        long start = 0L, end = 0L;
        if (d0 != null) {
            start = (long) (getTime(d0) / 1000) * 1_000_000_000;
            start += d0.getNano();
        }
        if (d1 != null) {
            end = (long) (getTime(d1) / 1000) * 1_000_000_000;
            end += d1.getNano();
        }
        return start - end;
    }

    public static boolean isSameDay(Object d1, Object d2) {
        boolean equals = Objects.equals(d1, d2);
        if (equals) {
            return equals;
        }
        if (d1 == null || d2 == null || d1.getClass() == d2.getClass()) {
            return false;
        }
        LocalDateTime t1 = null, t2 = null;
        {
            if (d1 instanceof Date) {
                t1 = toLocalDateTime((Date) d1);
            } else if (d1 instanceof String) {
                t1 = parse((String) d1);
            } else if (d1 instanceof LocalDateTime) {
                t1 = (LocalDateTime) d1;
            } else if (d1 instanceof Calendar) {
                t1 = toLocalDateTime((Calendar) d1);
            } else if (d1 instanceof LocalDate) {
                t1 = toLocalDateTime((LocalDate) d1);
            } else if (d1 instanceof Instant) {
                t1 = toLocalDateTime((Instant) d1);
            } else if (d1 instanceof ZonedDateTime) {
                t1 = toLocalDateTime((ZonedDateTime) d1);
            } else {
                return false;
            }
            if (d2 instanceof Date) {
                t2 = toLocalDateTime((Date) d2);
            } else if (d2 instanceof String) {
                t2 = parse((String) d2);
            } else if (d2 instanceof LocalDateTime) {
                t2 = (LocalDateTime) d2;
            } else if (d2 instanceof Calendar) {
                t2 = toLocalDateTime((Calendar) d2);
            } else if (d2 instanceof LocalDate) {
                t2 = toLocalDateTime((LocalDate) d2);
            } else if (d2 instanceof Instant) {
                t2 = toLocalDateTime((Instant) d2);
            } else if (d2 instanceof ZonedDateTime) {
                t2 = toLocalDateTime((ZonedDateTime) d2);
            } else {
                return false;
            }
        }
        return t1.toLocalDate().isEqual(t2.toLocalDate());
    }

    public static boolean isSame(Object d1, Object d2) {
        boolean equals = Objects.equals(d1, d2);
        if (equals) {
            return equals;
        }
        if (d1 == null || d2 == null || d1.getClass() == d2.getClass()) {
            return false;
        }
        LocalDateTime t1 = null, t2 = null;
        {
            if (d1 instanceof Date) {
                t1 = toLocalDateTime((Date) d1);
            } else if (d1 instanceof String) {
                t1 = parse((String) d1);
            } else if (d1 instanceof LocalDateTime) {
                t1 = (LocalDateTime) d1;
            } else if (d1 instanceof Calendar) {
                t1 = toLocalDateTime((Calendar) d1);
            } else if (d1 instanceof LocalDate) {
                t1 = toLocalDateTime((LocalDate) d1);
            } else if (d1 instanceof Instant) {
                t1 = toLocalDateTime((Instant) d1);
            } else if (d1 instanceof ZonedDateTime) {
                t1 = toLocalDateTime((ZonedDateTime) d1);
            } else {
                return false;
            }
            if (d2 instanceof Date) {
                t2 = toLocalDateTime((Date) d2);
            } else if (d2 instanceof String) {
                t2 = parse((String) d2);
            } else if (d2 instanceof LocalDateTime) {
                t2 = (LocalDateTime) d2;
            } else if (d2 instanceof Calendar) {
                t2 = toLocalDateTime((Calendar) d2);
            } else if (d2 instanceof LocalDate) {
                t2 = toLocalDateTime((LocalDate) d2);
            } else if (d2 instanceof Instant) {
                t2 = toLocalDateTime((Instant) d2);
            } else if (d2 instanceof ZonedDateTime) {
                t2 = toLocalDateTime((ZonedDateTime) d2);
            } else {
                return false;
            }
        }
        return t1.isEqual(t2);
    }

    static void testAll() {
        Consumer<Object[/*functionResult, expectResult, msg, param1,param2*/]> test = (datas) -> {
            Object d0 = datas[0], d1 = datas[1], msg = datas[2], p1 = datas[3], p2 = datas.length > 4 ? datas[4] : null;
            if (Objects.equals(d0, d1)) {
                return;
            }
            if ((d0 instanceof LocalDate || d1 instanceof LocalDate) && isSameDay(d0, d1)) {
                return;
            } else if (isSame(d0, d1)) {
                return;
            }
            System.out.println("ERROR: msg=" + msg + ", d0=" + (d0 instanceof Calendar ? ((Calendar) d0).getTime() : d0) + ", d1=" + d1 + ", p1=" + p1 + ", p2=" + p2);
        };
        long dateLong = 20250202020202L;
        int dateInt = 20250202;
        String dateStr = "2025-02-02 02:02:02";
        String dateStrStart = "2025-02-02 00:00:00.000", dateStrEnd = "2025-02-02 23:59:59.999";
        String weekStrStart = "2025-01-27 00:00:00.000", weekStrEnd = "2025-02-02 23:59:59.999";
        String monthStrStart = "2025-02-01 00:00:00.000", monthStrEnd = "2025-02-28 23:59:59.999";
        String yearStrStart = "2025-01-01 00:00:00.000", yearStrEnd = "2025-12-31 23:59:59.999";
        String plusYear = "2026-02-02 02:02:02", plusMonth = "2025-03-02 02:02:02", plusWeek = "2025-02-09 02:02:02", plusDay = "2025-02-03 02:02:02", plusHour = "2025-02-02 03:02:02", plusMinute = "2025-02-02 02:03:02", plusSecond = "2025-02-02 02:02:03", plusMillisecond = "2025-02-02 02:02:02.001";
        String tr2Second = "2025-02-02 02:02:02", tr2Minute = "2025-02-02 02:02:00", tr2Hour = "2025-02-02 02:00:00", tr2Day = dateStrStart, tr2Week = weekStrStart, tr2Month = monthStrStart, tr2Year = yearStrStart;
        String dayStr = "2025-02-02";
        String datePattern = "yyyy-MM-dd HH:mm:ss";
        String dayPattern = "yyyy-MM-dd";
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern(datePattern);
        Date date = fromLongDate(dateLong);
        Calendar cal = fromLongCalendar(dateLong);
        LocalDateTime ldt = LocalDateTime.of(2025, 02, 02, 02, 02, 02);
        ZonedDateTime zdt = ZonedDateTime.of(2025, 02, 02, 02, 02, 02, 0, ZoneId.systemDefault());
        LocalDate ld = LocalDate.of(2025, 02, 02);
        Instant ins = toInstant(date);
        LocalDateTime nanoLdt = LocalDateTime.of(2025, 02, 02, 02, 02, 02, 999_000_000);
        LocalDateTime nanoLd = LocalDateTime.of(2025, 02, 02, 0, 0, 0, 999_000_000);
        //
        test.accept(new Object[]{format(ldt, datePattern), dateStr, "format(date:LocalDateTime, pattern:String):String", ldt, datePattern});
        test.accept(new Object[]{format(ld, datePattern), ld, "format(date:LocalDateT, pattern:String):String", ld, datePattern});
        test.accept(new Object[]{format(zdt, datePattern), date, "format(date:ZonedDateTime, pattern:String):String", zdt, datePattern});
        test.accept(new Object[]{format(ins, datePattern), date, "format(date:Instant, pattern:String):String", ins, datePattern});
        test.accept(new Object[]{format(date, datePattern), ldt, "format(date:Date, pattern:String):String", date, datePattern});
        test.accept(new Object[]{format(cal, datePattern), date, "format(date:Calendar, pattern:String):String", cal, datePattern});
        test.accept(new Object[]{format(ldt), date, "format(date:LocalDateTime):String", ldt});
        test.accept(new Object[]{format(ld), ld, "format(date:LocalDate):String", ld});
        test.accept(new Object[]{format(zdt), date, "format(date:ZonedDateTime):String", zdt});
        test.accept(new Object[]{format(ins), date, "format(date:Instant):String", ins});
        test.accept(new Object[]{format(date), date, "format(date:Date):String", date});
        test.accept(new Object[]{format(cal), date, "format(date:Calendar):String", cal});
        //
        test.accept(new Object[]{formatWithoutTime(ldt), dayStr, "formatWithoutTime(date:LocalDateTime):String", ldt});
        test.accept(new Object[]{formatWithoutTime(ld), dayStr, "formatWithoutTime(date:LocalDate):String", ld});
        test.accept(new Object[]{formatWithoutTime(zdt), dayStr, "formatWithoutTime(date:ZonedDateTime):String", zdt});
        test.accept(new Object[]{formatWithoutTime(ins), dayStr, "formatWithoutTime(date:Instant):String", ins});
        test.accept(new Object[]{formatWithoutTime(date), dayStr, "formatWithoutTime(date:Date):String", date});
        //
        test.accept(new Object[]{parse(dateStr), ldt, "parse(dateStr:String):LocalDateTime", dateStr});
        test.accept(new Object[]{parseLocalDate(dateStr), ld, "parseLocalDate(dateStr:String):LocalDate", dateStr});
        test.accept(new Object[]{parseDate(dateStr), date, "parseDate(dateStr:String):Date", dateStr});
        test.accept(new Object[]{parseCalendar(dateStr), cal, "parseCalendar(dateStr:String):Calendar", dateStr});
        test.accept(new Object[]{parseInstant(dateStr), ins, "parseInstant(dateStr:String):Instant", dateStr});
        test.accept(new Object[]{parse(dateStr, dtf), date, "parse(dateStr:String, formatter:DateTimeFormatter):LocalDateTime", dateStr, dtf});
        test.accept(new Object[]{parseNoTime(dateStr, dtf), ld, "parseNoTime(dateStr:String, formatter:DateTimeFormatter):LocalDateTime", dateStr, dtf});
        //
        test.accept(new Object[]{getTime(parse("1970-01-01 00:00:01")), 1000L, "getTime(date:LocalDateTime):Long", parse("1970-01-01 00:00:01")});
        test.accept(new Object[]{getTime(Instant.ofEpochMilli(1000)), 1000L, "getTime(date:Instant):Long", Instant.ofEpochMilli(1000)});
        test.accept(new Object[]{getTime(toCalendar(new Date(1000))), 1000L, "getTime(date:Calendar):Long", toCalendar(new Date(1000))});
        test.accept(new Object[]{getTime(new Date(1000)), 1000L, "getTime(date:Date):Long", new Date(1000)});
        //
        test.accept(new Object[]{toDate(ldt), date, "toDate(date:LocalDateTime):Date", ldt});
        test.accept(new Object[]{toDate(ld), ld, "toDate(date:LocalDate):Date", ld});
        test.accept(new Object[]{toDate(zdt), date, "toDate(date:ZonedDateTime):Date", zdt});
        test.accept(new Object[]{toDate(ins), date, "toDate(date:Instant):Date", ins});
        test.accept(new Object[]{toDate(date), date, "toDate(date:Date):Date", date});
        test.accept(new Object[]{toDate(cal), date, "toDate(date:Calendar):Date", date});
        test.accept(new Object[]{toDate(1000L), new Date(1000), "toDate(millisecond:Long):Date", 1000L});
        //
        test.accept(new Object[]{toCalendar(ldt), date, "toCalendar(date:LocalDateTime):Date", ldt});
        test.accept(new Object[]{toCalendar(ld), ld, "toCalendar(date:LocalDate):Date", ld});
        test.accept(new Object[]{toCalendar(zdt), date, "toCalendar(date:ZonedDateTime):Date", zdt});
        test.accept(new Object[]{toCalendar(ins), date, "toCalendar(date:Instant):Date", ins});
        test.accept(new Object[]{toCalendar(date), date, "toCalendar(date:Date):Date", date});
        test.accept(new Object[]{toCalendar(cal), date, "toCalendar(date:Calendar):Date", date});
        test.accept(new Object[]{toCalendar(1000L), new Date(1000), "toCalendar(millisecond:Long):Date", 1000L});
        //
        test.accept(new Object[]{toInstant(ldt), date, "toInstant(date:LocalDateTime):Date", ldt});
        test.accept(new Object[]{toInstant(ld), ld, "toInstant(date:LocalDate):Date", ld});
        test.accept(new Object[]{toInstant(zdt), date, "toInstant(date:ZonedDateTime):Date", zdt});
        test.accept(new Object[]{toInstant(ins), date, "toInstant(date:Instant):Date", ins});
        test.accept(new Object[]{toInstant(date), date, "toInstant(date:Date):Date", date});
        test.accept(new Object[]{toInstant(cal), date, "toInstant(date:Calendar):Date", date});
        test.accept(new Object[]{toInstant(1000L), new Date(1000), "toInstant(millisecond:Long):Date", 1000L});
        //
        test.accept(new Object[]{toLocalDateTime(ldt), date, "toLocalDateTime(date:LocalDateTime):Date", ldt});
        test.accept(new Object[]{toLocalDateTime(ld), ld, "toLocalDateTime(date:LocalDate):Date", ld});
        test.accept(new Object[]{toLocalDateTime(zdt), date, "toLocalDateTime(date:ZonedDateTime):Date", zdt});
        test.accept(new Object[]{toLocalDateTime(ins), date, "toLocalDateTime(date:Instant):Date", ins});
        test.accept(new Object[]{toLocalDateTime(date), date, "toLocalDateTime(date:Date):Date", date});
        test.accept(new Object[]{toLocalDateTime(cal), date, "toLocalDateTime(date:Calendar):Date", date});
        test.accept(new Object[]{toLocalDateTime(1000L), new Date(1000), "toLocalDateTime(millisecond:Long):Date", 1000L});
        //
        test.accept(new Object[]{toLocalDate(ldt), date, "toLocalDate(date:LocalDateTime):Date", ldt});
        test.accept(new Object[]{toLocalDate(ld), ld, "toLocalDate(date:LocalDate):Date", ld});
        test.accept(new Object[]{toLocalDate(zdt), date, "toLocalDate(date:ZonedDateTime):Date", zdt});
        test.accept(new Object[]{toLocalDate(ins), date, "toLocalDate(date:Instant):Date", ins});
        test.accept(new Object[]{toLocalDate(date), date, "toLocalDate(date:Date):Date", date});
        test.accept(new Object[]{toLocalDate(cal), date, "toLocalDate(date:Calendar):Date", date});
        test.accept(new Object[]{toLocalDate(1000L), new Date(1000), "toLocalDate(millisecond:Long):Date", 1000L});
        //
        test.accept(new Object[]{fromIntDate(dateInt), ld, "fromIntDate(yyyyMMdd:Integer):Date", dateInt});
        test.accept(new Object[]{fromIntCalendar(dateInt), ld, "fromIntDate(yyyyMMdd:Integer):Calendar", dateInt});
        test.accept(new Object[]{fromIntInstant(dateInt), ld, "fromIntDate(yyyyMMdd:Integer):Instant", dateInt});
        test.accept(new Object[]{fromIntLocalDateTime(dateInt), ld, "fromIntDate(yyyyMMdd:Integer):LocalDateTime", dateInt});
        test.accept(new Object[]{fromIntLocalDate(dateInt), ld, "fromIntDate(yyyyMMdd:Integer):LocalDate", dateInt});
        //
        test.accept(new Object[]{fromLongDate(dateLong), ld, "fromLongDate(yyyyMMdd:Long):Date", dateLong});
        test.accept(new Object[]{fromLongCalendar(dateLong), ld, "fromLongDate(yyyyMMdd:Long):Calendar", dateLong});
        test.accept(new Object[]{fromLongInstant(dateLong), ld, "fromLongDate(yyyyMMdd:Long):Instant", dateLong});
        test.accept(new Object[]{fromLongLocalDateTime(dateLong), ld, "fromLongDate(yyyyMMdd:Long):LocalDateTime", dateLong});
        test.accept(new Object[]{fromLongLocalDate(dateLong), ld, "fromLongDate(yyyyMMdd:Long):LocalDate", dateLong});
        //
        test.accept(new Object[]{toDateInt(ldt), dateInt, "toDateInt(date:LocalDateTime):Integer", ldt});
        test.accept(new Object[]{toDateInt(ld), dateInt, "toDateInt(date:LocalDate):Integer", ld});
        test.accept(new Object[]{toDateInt(ins), dateInt, "toDateInt(date:Instant):Integer", ins});
        test.accept(new Object[]{toDateInt(date), dateInt, "toDateInt(date:Date):Integer", date});
        test.accept(new Object[]{toDateInt(cal), dateInt, "toDateInt(date:Calendar):Integer", cal});
        //
        test.accept(new Object[]{toDateTimeLong(ldt), dateLong, "toDateLong(date:LocalDateTime):Long", ldt});
        test.accept(new Object[]{toDateTimeLong(ld), dateInt * 1_00_00_00L, "toDateLong(date:LocalDate):Long", ld});
        test.accept(new Object[]{toDateTimeLong(ins), dateLong, "toDateLong(date:Instant):Long", ins});
        test.accept(new Object[]{toDateTimeLong(date), dateLong, "toDateLong(date:Date):Long", date});
        test.accept(new Object[]{toDateTimeLong(cal), dateLong, "toDateLong(date:Calendar):Long", cal});
        //
        test.accept(new Object[]{dayStart(ldt), dateStrStart, "dayStart(date:LocalDateTime):LocalDateTime", ldt});
        test.accept(new Object[]{dayStart(ld), dateStrStart, "dayStart(date:LocalDate):LocalDateTime", ld});
        test.accept(new Object[]{dayStart(ins), dateStrStart, "dayStart(date:Instant):Instant", ins});
        test.accept(new Object[]{dayStart(date), dateStrStart, "dayStart(date:Date):Date", date});
        test.accept(new Object[]{dayStart(cal), dateStrStart, "dayStart(date:Calendar):Calendar", cal});
        //
        test.accept(new Object[]{dayEnd(ldt).minusNanos(999999), dateStrEnd, "dayEnd(date:LocalDateTime):LocalDateTime", ldt});
        test.accept(new Object[]{dayEnd(ld).minusNanos(999999), dateStrEnd, "dayEnd(date:LocalDate):LocalDateTime", ld});
        test.accept(new Object[]{dayEnd(ins).minusNanos(999999), dateStrEnd, "dayEnd(date:Instant):Instant", ins});
        test.accept(new Object[]{dayEnd(date), dateStrEnd, "dayEnd(date:Date):Date", date});
        test.accept(new Object[]{dayEnd(cal), dateStrEnd, "dayEnd(date:Calendar):Calendar", cal});
        //
        test.accept(new Object[]{weekStart(ldt), weekStrStart, "weekStart(date:LocalDateTime):LocalDateTime", ldt});
        test.accept(new Object[]{weekStart(ld), weekStrStart, "weekStart(date:LocalDate):LocalDateTime", ld});
        test.accept(new Object[]{weekStart(ins), weekStrStart, "weekStart(date:Instant):Instant", ins});
        test.accept(new Object[]{weekStart(date), weekStrStart, "weekStart(date:Date):Date", date});
        test.accept(new Object[]{weekStart(cal), weekStrStart, "weekStart(date:Calendar):Calendar", cal});
        //
        test.accept(new Object[]{weekEnd(ldt).minusNanos(999999), weekStrEnd, "weekEnd(date:LocalDateTime):LocalDateTime", ldt});
        test.accept(new Object[]{weekEnd(ld).minusNanos(999999), weekStrEnd, "weekEnd(date:LocalDate):LocalDateTime", ld});
        test.accept(new Object[]{weekEnd(ins).minusNanos(999999), weekStrEnd, "weekEnd(date:Instant):Instant", ins});
        test.accept(new Object[]{weekEnd(date), weekStrEnd, "weekEnd(date:Date):Date", date});
        test.accept(new Object[]{weekEnd(cal), weekStrEnd, "weekEnd(date:Calendar):Calendar", cal});
        //
        test.accept(new Object[]{monthStart(ldt), monthStrStart, "monthStart(date:LocalDateTime):LocalDateTime", ldt});
        test.accept(new Object[]{monthStart(ld), monthStrStart, "monthStart(date:LocalDate):LocalDateTime", ld});
        test.accept(new Object[]{monthStart(ins), monthStrStart, "monthStart(date:Instant):Instant", ins});
        test.accept(new Object[]{monthStart(date), monthStrStart, "monthStart(date:Date):Date", date});
        test.accept(new Object[]{monthStart(cal), monthStrStart, "monthStart(date:Calendar):Calendar", cal});
        //
        test.accept(new Object[]{monthEnd(ldt).minusNanos(999999), monthStrEnd, "monthEnd(date:LocalDateTime):LocalDateTime", ldt});
        test.accept(new Object[]{monthEnd(ld).minusNanos(999999), monthStrEnd, "monthEnd(date:LocalDate):LocalDateTime", ld});
        test.accept(new Object[]{monthEnd(ins).minusNanos(999999), monthStrEnd, "monthEnd(date:Instant):Instant", ins});
        test.accept(new Object[]{monthEnd(date), monthStrEnd, "monthEnd(date:Date):Date", date});
        test.accept(new Object[]{monthEnd(cal), monthStrEnd, "monthEnd(date:Calendar):Calendar", cal});
        //
        test.accept(new Object[]{yearStart(ldt), yearStrStart, "yearStart(date:LocalDateTime):LocalDateTime", ldt});
        test.accept(new Object[]{yearStart(ld), yearStrStart, "yearStart(date:LocalDate):LocalDateTime", ld});
        test.accept(new Object[]{yearStart(ins), yearStrStart, "yearStart(date:Instant):Instant", ins});
        test.accept(new Object[]{yearStart(date), yearStrStart, "yearStart(date:Date):Date", date});
        test.accept(new Object[]{yearStart(cal), yearStrStart, "yearStart(date:Calendar):Calendar", cal});
        //
        test.accept(new Object[]{yearEnd(ldt).minusNanos(999999), yearStrEnd, "yearEnd(date:LocalDateTime):LocalDateTime", ldt});
        test.accept(new Object[]{yearEnd(ld).minusNanos(999999), yearStrEnd, "yearEnd(date:LocalDate):LocalDateTime", ld});
        test.accept(new Object[]{yearEnd(ins).minusNanos(999999), yearStrEnd, "yearEnd(date:Instant):Instant", ins});
        test.accept(new Object[]{yearEnd(date), yearStrEnd, "yearEnd(date:Date):Date", date});
        test.accept(new Object[]{yearEnd(cal), yearStrEnd, "yearEnd(date:Calendar):Calendar", cal});
        //
        test.accept(new Object[]{plusYear(ldt, 1), plusYear, "plusYear(date:LocalDateTime, year:int):LocalDateTime", ldt, 1});
        test.accept(new Object[]{plusYear(ld, 1), plusYear.split(" ")[0], "plusYear(date:LocalDate, year:int):LocalDate", ldt, 1});
        test.accept(new Object[]{plusYear(ins, 1), plusYear, "plusYear(date:Instant, year:int):Instant", ins, 1});
        test.accept(new Object[]{plusYear(date, 1), plusYear, "plusYear(date:Date, year:int):Date", date, 1});
        test.accept(new Object[]{plusYear(cal, 1), plusYear, "plusYear(date:Calendar, year:int):Calendar", cal, 1});
        //
        test.accept(new Object[]{plusMonth(ldt, 1), plusMonth, "plusMonth(date:LocalDateTime, month:int):LocalDateTime", ldt, 1});
        test.accept(new Object[]{plusMonth(ld, 1), plusMonth.split(" ")[0], "plusMonth(date:LocalDate, month:int):LocalDate", ldt, 1});
        test.accept(new Object[]{plusMonth(ins, 1), plusMonth, "plusMonth(date:Instant, month:int):Instant", ins, 1});
        test.accept(new Object[]{plusMonth(date, 1), plusMonth, "plusMonth(date:Date, month:int):Date", date, 1});
        test.accept(new Object[]{plusMonth(cal, 1), plusMonth, "plusMonth(date:Calendar, month:int):Calendar", cal, 1});
        //
        test.accept(new Object[]{plusWeek(ldt, 1), plusWeek, "plusWeek(date:LocalDateTime, week:int):LocalDateTime", ldt, 1});
        test.accept(new Object[]{plusWeek(ld, 1), plusWeek.split(" ")[0], "plusWeek(date:LocalDate, week:int):LocalDate", ldt, 1});
        test.accept(new Object[]{plusWeek(ins, 1), plusWeek, "plusWeek(date:Instant, week:int):Instant", ins, 1});
        test.accept(new Object[]{plusWeek(date, 1), plusWeek, "plusWeek(date:Date, week:int):Date", date, 1});
        test.accept(new Object[]{plusWeek(cal, 1), plusWeek, "plusWeek(date:Calendar, week:int):Calendar", cal, 1});
        //
        test.accept(new Object[]{plusDay(ldt, 1), plusDay, "plusDay(date:LocalDateTime, day:int):LocalDateTime", ldt, 1});
        test.accept(new Object[]{plusDay(ld, 1), plusDay.split(" ")[0], "plusDay(date:LocalDate, day:int):LocalDate", ldt, 1});
        test.accept(new Object[]{plusDay(ins, 1), plusDay, "plusDay(date:Instant, day:int):Instant", ins, 1});
        test.accept(new Object[]{plusDay(date, 1), plusDay, "plusDay(date:Date, day:int):Date", date, 1});
        test.accept(new Object[]{plusDay(cal, 1), plusDay, "plusDay(date:Calendar, day:int):Calendar", cal, 1});
        //
        test.accept(new Object[]{plusHour(ldt, 1), plusHour, "plusHour(date:LocalDateTime, hour:int):LocalDateTime", ldt, 1});
        test.accept(new Object[]{plusHour(ld, 1), plusHour.replace("03:02:02", "01:00:00"), "plusHour(date:LocalDate, hour:int):LocalDate", ldt, 1});
        test.accept(new Object[]{plusHour(ins, 1), plusHour, "plusHour(date:Instant, hour:int):Instant", ins, 1});
        test.accept(new Object[]{plusHour(date, 1), plusHour, "plusHour(date:Date, hour:int):Date", date, 1});
        test.accept(new Object[]{plusHour(cal, 1), plusHour, "plusHour(date:Calendar, hour:int):Calendar", cal, 1});
        //
        test.accept(new Object[]{plusMinute(ldt, 1), plusMinute, "plusMinute(date:LocalDateTime, minute:int):LocalDateTime", ldt, 1});
        test.accept(new Object[]{plusMinute(ld, 1), plusMinute.replace("02:03:02", "00:01:00"), "plusMinute(date:LocalDate, minute:int):LocalDate", ldt, 1});
        test.accept(new Object[]{plusMinute(ins, 1), plusMinute, "plusMinute(date:Instant, minute:int):Instant", ins, 1});
        test.accept(new Object[]{plusMinute(date, 1), plusMinute, "plusMinute(date:Date, minute:int):Date", date, 1});
        test.accept(new Object[]{plusMinute(cal, 1), plusMinute, "plusMinute(date:Calendar, minute:int):Calendar", cal, 1});
        //
        test.accept(new Object[]{plusSecond(ldt, 1), plusSecond, "plusSecond(date:LocalDateTime, second:int):LocalDateTime", ldt, 1});
        test.accept(new Object[]{plusSecond(ld, 1), plusSecond.replace("02:02:03", "00:00:01"), "plusSecond(date:LocalDate, second:int):LocalDate", ldt, 1});
        test.accept(new Object[]{plusSecond(ins, 1), plusSecond, "plusSecond(date:Instant, second:int):Instant", ins, 1});
        test.accept(new Object[]{plusSecond(date, 1), plusSecond, "plusSecond(date:Date, second:int):Date", date, 1});
        test.accept(new Object[]{plusSecond(cal, 1), plusSecond, "plusSecond(date:Calendar, second:int):Calendar", cal, 1});
        //
        test.accept(new Object[]{plusMillisecond(ldt, 1), plusMillisecond, "plusMillisecond(date:LocalDateTime, millisecond:int):LocalDateTime", ldt, 1});
        test.accept(new Object[]{plusMillisecond(ld, 1), plusMillisecond.replace("02:02:02", "00:00:00"), "plusMillisecond(date:LocalDate, millisecond:int):LocalDate", ldt, 1});
        test.accept(new Object[]{plusMillisecond(ins, 1), plusMillisecond, "plusMillisecond(date:Instant, millisecond:int):Instant", ins, 1});
        test.accept(new Object[]{plusMillisecond(date, 1), plusMillisecond, "plusMillisecond(date:Date, millisecond:int):Date", date, 1});
        test.accept(new Object[]{plusMillisecond(cal, 1), plusMillisecond, "plusMillisecond(date:Calendar, millisecond:int):Calendar", cal, 1});
        //
        test.accept(new Object[]{truncatedToSecond(ldt), tr2Second, "truncatedToSecond(date:LocalDateTime):LocalDateTime", ldt});
        test.accept(new Object[]{truncatedToSecond(ld), tr2Second.replace("02:02:02", "00:00:00"), "truncatedToSecond(date:LocalDate):LocalDateTime", ldt});
        test.accept(new Object[]{truncatedToSecond(ins), tr2Second, "truncatedToSecond(date:Instant):Instant", ins});
        test.accept(new Object[]{truncatedToSecond(date), tr2Second, "truncatedToSecond(date:Date):Date", date});
        test.accept(new Object[]{truncatedToSecond(cal), tr2Second, "truncatedToSecond(date:Calendar):Calendar", cal});
        //
        test.accept(new Object[]{truncatedToMinute(ldt), tr2Minute, "truncatedToMinute(date:LocalDateTime):LocalDateTime", ldt});
        test.accept(new Object[]{truncatedToMinute(ld), tr2Minute.replace("02:02:00", "00:00:00"), "truncatedToMinute(date:LocalDate):LocalDateTime", ldt});
        test.accept(new Object[]{truncatedToMinute(ins), tr2Minute, "truncatedToMinute(date:Instant):Instant", ins});
        test.accept(new Object[]{truncatedToMinute(date), tr2Minute, "truncatedToMinute(date:Date):Date", date});
        test.accept(new Object[]{truncatedToMinute(cal), tr2Minute, "truncatedToMinute(date:Calendar):Calendar", cal});
        //
        test.accept(new Object[]{truncatedToHour(ldt), tr2Hour, "truncatedToHour(date:LocalDateTime):LocalDateTime", ldt});
        test.accept(new Object[]{truncatedToHour(ld), tr2Hour.replace("02:00:00", "00:00:00"), "truncatedToHour(date:LocalDate):LocalDateTime", ldt});
        test.accept(new Object[]{truncatedToHour(ins), tr2Hour, "truncatedToHour(date:Instant):Instant", ins});
        test.accept(new Object[]{truncatedToHour(date), tr2Hour, "truncatedToHour(date:Date):Date", date});
        test.accept(new Object[]{truncatedToHour(cal), tr2Hour, "truncatedToHour(date:Calendar):Calendar", cal});
        //
        test.accept(new Object[]{truncatedToDay(ldt), tr2Day, "truncatedToDay(date:LocalDateTime):LocalDateTime", ldt});
        test.accept(new Object[]{truncatedToDay(ld), tr2Day, "truncatedToDay(date:LocalDate):LocalDateTime", ldt});
        test.accept(new Object[]{truncatedToDay(ins), tr2Day, "truncatedToDay(date:Instant):Instant", ins});
        test.accept(new Object[]{truncatedToDay(date), tr2Day, "truncatedToDay(date:Date):Date", date});
        test.accept(new Object[]{truncatedToDay(cal), tr2Day, "truncatedToDay(date:Calendar):Calendar", cal});
        //
        test.accept(new Object[]{truncatedToWeek(ldt), tr2Week, "truncatedToWeek(date:LocalDateTime):LocalDateTime", ldt});
        test.accept(new Object[]{truncatedToWeek(ld), tr2Week, "truncatedToWeek(date:LocalDate):LocalDateTime", ldt});
        test.accept(new Object[]{truncatedToWeek(ins), tr2Week, "truncatedToWeek(date:Instant):Instant", ins});
        test.accept(new Object[]{truncatedToWeek(date), tr2Week, "truncatedToWeek(date:Date):Date", date});
        test.accept(new Object[]{truncatedToWeek(cal), tr2Week, "truncatedToWeek(date:Calendar):Calendar", cal});
        //
        test.accept(new Object[]{truncatedToMonth(ldt), tr2Month, "truncatedToMonth(date:LocalDateTime):LocalDateTime", ldt});
        test.accept(new Object[]{truncatedToMonth(ld), tr2Month, "truncatedToMonth(date:LocalDate):LocalDateTime", ldt});
        test.accept(new Object[]{truncatedToMonth(ins), tr2Month, "truncatedToMonth(date:Instant):Instant", ins});
        test.accept(new Object[]{truncatedToMonth(date), tr2Month, "truncatedToMonth(date:Date):Date", date});
        test.accept(new Object[]{truncatedToMonth(cal), tr2Month, "truncatedToMonth(date:Calendar):Calendar", cal});
        //
        test.accept(new Object[]{truncatedToYear(ldt), tr2Year, "truncatedToYear(date:LocalDateTime):LocalDateTime", ldt});
        test.accept(new Object[]{truncatedToYear(ld), tr2Year, "truncatedToYear(date:LocalDate):LocalDateTime", ldt});
        test.accept(new Object[]{truncatedToYear(ins), tr2Year, "truncatedToYear(date:Instant):Instant", ins});
        test.accept(new Object[]{truncatedToYear(date), tr2Year, "truncatedToYear(date:Date):Date", date});
        test.accept(new Object[]{truncatedToYear(cal), tr2Year, "truncatedToYear(date:Calendar):Calendar", cal});
        //
        test.accept(new Object[]{setNano(ldt, 999_000_000), of(2025, 2, 2, 2, 2, 2, 999_000_000), "setNano(date:LocalDateTime, nano:int):LocalDateTime", ldt, 999_000_000});
        test.accept(new Object[]{setNano(ld, 999_000_000), of(2025, 2, 2, 0, 0, 0, 999_000_000), "setNano(date:LocalDate, nano:int):LocalDateTime", ldt, 999_000_000});
        test.accept(new Object[]{setNano(ins, 999_000_000), of(2025, 2, 2, 2, 2, 2, 999_000_000), "setNano(date:Instant, nano:int):Instant", ins, 999_000_000});
        test.accept(new Object[]{setNano(date, 999_000_000), of(2025, 2, 2, 2, 2, 2, 999_000_000), "setNano(date:Date, nano:int):LocalDateTime", date, 999_000_000});
        test.accept(new Object[]{setNano(cal, 999_000_000), of(2025, 2, 2, 2, 2, 2, 999_000_000), "setNano(date:Calendar, nano:int):LocalDateTime", cal, 999_000_000});
        //
        test.accept(new Object[]{setMillisecond(ldt, 999), of(2025, 2, 2, 2, 2, 2, 999_000_000), "setMillisecond(date:LocalDateTime, millisecond:int):LocalDateTime", ldt, 999_000_000});
        test.accept(new Object[]{setMillisecond(ld, 999), of(2025, 2, 2, 0, 0, 0, 999_000_000), "setMillisecond(date:LocalDate, millisecond:int):LocalDateTime", ldt, 999_000_000});
        test.accept(new Object[]{setMillisecond(ins, 999), of(2025, 2, 2, 2, 2, 2, 999_000_000), "setMillisecond(date:Instant, millisecond:int):Instant", ins, 999_000_000});
        test.accept(new Object[]{setMillisecond(date, 999), of(2025, 2, 2, 2, 2, 2, 999_000_000), "setMillisecond(date:Date, millisecond:int):Date", date, 999_000_000});
        test.accept(new Object[]{setMillisecond(cal, 999), of(2025, 2, 2, 2, 2, 2, 999_000_000), "setMillisecond(date:Calendar, millisecond:int):Calendar", cal, 999_000_000});
        //
        test.accept(new Object[]{setSecond(ldt, 9), of(2025, 2, 2, 2, 2, 9), "setSecond(date:LocalDateTime, second:int):LocalDateTime", ldt, 9});
        test.accept(new Object[]{setSecond(ld, 9), of(2025, 2, 2, 0, 0, 9), "setSecond(date:LocalDate, second:int):LocalDateTime", ldt, 9});
        test.accept(new Object[]{setSecond(ins, 9), of(2025, 2, 2, 2, 2, 9), "setSecond(date:Instant, second:int):Instant", ins, 9});
        test.accept(new Object[]{setSecond(date, 9), of(2025, 2, 2, 2, 2, 9), "setSecond(date:Date, second:int):Date", date, 9});
        test.accept(new Object[]{setSecond(cal, 9), of(2025, 2, 2, 2, 2, 9), "setSecond(date:Calendar, second:int):Calendar", cal, 9});
        //
        test.accept(new Object[]{setMinute(ldt, 9), of(2025, 2, 2, 2, 9, 2), "setMinute(date:LocalDateTime, minute:int):LocalDateTime", ldt, 9});
        test.accept(new Object[]{setMinute(ld, 9), of(2025, 2, 2, 0, 9, 0), "setMinute(date:LocalDate, minute:int):LocalDateTime", ldt, 9});
        test.accept(new Object[]{setMinute(ins, 9), of(2025, 2, 2, 2, 9, 2), "setMinute(date:Instant, minute:int):Instant", ins, 9});
        test.accept(new Object[]{setMinute(date, 9), of(2025, 2, 2, 2, 9, 2), "setMinute(date:Date, minute:int):Date", date, 9});
        test.accept(new Object[]{setMinute(cal, 9), of(2025, 2, 2, 2, 9, 2), "setMinute(date:Calendar, minute:int):Calendar", cal, 9});
        //
        test.accept(new Object[]{setHour(ldt, 9), of(2025, 2, 2, 9, 2, 2), "setHour(date:LocalDateTime, hour:int):LocalDateTime", ldt, 9});
        test.accept(new Object[]{setHour(ld, 9), of(2025, 2, 2, 9, 0, 0), "setHour(date:LocalDate, hour:int):LocalDateTime", ldt, 9});
        test.accept(new Object[]{setHour(ins, 9), of(2025, 2, 2, 9, 2, 2), "setHour(date:Instant, hour:int):Instant", ins, 9});
        test.accept(new Object[]{setHour(date, 9), of(2025, 2, 2, 9, 2, 2), "setHour(date:Date, hour:int):Date", date, 9});
        test.accept(new Object[]{setHour(cal, 9), of(2025, 2, 2, 9, 2, 2), "setHour(date:Calendar, hour:int):Calendar", cal, 9});
        //
        test.accept(new Object[]{setDay(ldt, 9), of(2025, 2, 9, 2, 2, 2), "setDay(date:LocalDateTime, day:int):LocalDateTime", ldt, 9});
        test.accept(new Object[]{setDay(ld, 9), of(2025, 2, 9, 0, 0, 0), "setDay(date:LocalDate, day:int):LocalDateTime", ldt, 9});
        test.accept(new Object[]{setDay(ins, 9), of(2025, 2, 9, 2, 2, 2), "setDay(date:Instant, day:int):Instant", ins, 9});
        test.accept(new Object[]{setDay(date, 9), of(2025, 2, 9, 2, 2, 2), "setDay(date:Date, day:int):Date", date, 9});
        test.accept(new Object[]{setDay(cal, 9), of(2025, 2, 9, 2, 2, 2), "setDay(date:Calendar, day:int):Calendar", cal, 9});
        //
        test.accept(new Object[]{setMonth(ldt, 9), of(2025, 9, 2, 2, 2, 2), "setMonth(date:LocalDateTime, month:int):LocalDateTime", ldt, 9});
        test.accept(new Object[]{setMonth(ld, 9), of(2025, 9, 2, 0, 0, 0), "setMonth(date:LocalDate, month:int):LocalDateTime", ldt, 9});
        test.accept(new Object[]{setMonth(ins, 9), of(2025, 9, 2, 2, 2, 2), "setMonth(date:Instant, month:int):Instant", ins, 9});
        test.accept(new Object[]{setMonth(date, 9), of(2025, 9, 2, 2, 2, 2), "setMonth(date:Date, month:int):Date", date, 9});
        test.accept(new Object[]{setMonth(cal, 9), of(2025, 9, 2, 2, 2, 2), "setMonth(date:Calendar, month:int):Calendar", cal, 9});
        //
        test.accept(new Object[]{setYear(ldt, 9), of(9, 2, 2, 2, 2, 2), "setYear(date:LocalDateTime, year:int):LocalDateTime", ldt, 9});
        test.accept(new Object[]{setYear(ld, 9), of(9, 2, 2, 0, 0, 0), "setYear(date:LocalDate, year:int):LocalDateTime", ldt, 9});
        test.accept(new Object[]{setYear(ins, 9), of(9, 2, 2, 2, 2, 2), "setYear(date:Instant, year:int):Instant", ins, 9});
        test.accept(new Object[]{setYear(date, 9), of(9, 2, 2, 2, 2, 2), "setYear(date:Date, year:int):Date", date, 9});
        test.accept(new Object[]{setYear(cal, 9), of(9, 2, 2, 2, 2, 2), "setYear(date:Calendar, year:int):Calendar", cal, 9});
//        of(year:int, month:int, dayOfMonth:int, hour:int, minute:int, second:int, nanoOfSecond:int):LocalDateTime
//        of(year:int, month:int, dayOfMonth:int, hour:int, minute:int, second:int):LocalDateTime
//        of(year:int, month:int, dayOfMonth:int, hour:int, minute:int):LocalDateTime
//        of(year:int, month:int, dayOfMonth:int, hour:int):LocalDateTime
//        of(year:int, month:int, dayOfMonth:int):LocalDateTime
//        ofLocalDate(year:int, month:int, dayOfMonth:int, hour:int, minute:int, second:int, nanoOfSecond:int):LocalDate
//        ofLocalDate(year:int, month:int, dayOfMonth:int, hour:int, minute:int, second:int):LocalDate
//        ofLocalDate(year:int, month:int, dayOfMonth:int, hour:int, minute:int):LocalDate
//        ofLocalDate(year:int, month:int, dayOfMonth:int, hour:int):LocalDate
//        ofLocalDate(year:int, month:int, dayOfMonth:int):LocalDate
//        ofInstant(year:int, month:int, dayOfMonth:int, hour:int, minute:int, second:int, nanoOfSecond:int):Instant
//        ofInstant(year:int, month:int, dayOfMonth:int, hour:int, minute:int, second:int):Instant
//        ofInstant(year:int, month:int, dayOfMonth:int, hour:int, minute:int):Instant
//        ofInstant(year:int, month:int, dayOfMonth:int, hour:int):Instant
//        ofInstant(year:int, month:int, dayOfMonth:int):Instant
//        ofDate(year:int, month:int, dayOfMonth:int, hour:int, minute:int, second:int, nanoOfSecond:int):Date
//        ofDate(year:int, month:int, dayOfMonth:int, hour:int, minute:int, second:int):Date
//        ofDate(year:int, month:int, dayOfMonth:int, hour:int, minute:int):Date
//        ofDate(year:int, month:int, dayOfMonth:int, hour:int):Date
//        ofDate(year:int, month:int, dayOfMonth:int):Date
//        ofCalendar(year:int, month:int, dayOfMonth:int, hour:int, minute:int, second:int, nanoOfSecond:int):Calendar
//        ofCalendar(year:int, month:int, dayOfMonth:int, hour:int, minute:int, second:int):Calendar
//        ofCalendar(year:int, month:int, dayOfMonth:int, hour:int, minute:int):Calendar
//        ofCalendar(year:int, month:int, dayOfMonth:int, hour:int):Calendar
//        ofCalendar(year:int, month:int, dayOfMonth:int):Calendar
        //
        test.accept(new Object[]{subtract(of(2025, 2, 2, 2, 2, 2, 999_999_999), of(2025, 2, 2, 2, 2, 2, 666_000_000)), 333L, "subtract(d0:LocalDateTime, d1:LocalDateTime):long", null, null});
        test.accept(new Object[]{subtractNano(of(2025, 2, 2, 2, 2, 2, 999_999_999), of(2025, 2, 2, 2, 2, 2, 999_999_666)), 333L, "subtractNano(d0:LocalDateTime, d1:LocalDateTime):long", null, null});
//        isSameDay(d1:Object, d2:Object):boolean
//        isSame(d1:Object, d2:Object):boolean
    }

    public static void main(String[] args) {
        testAll();
    }
}
